From 6b381ecdbd81e47ec122a493d27529dd36db20b2 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 9 Feb 2016 13:43:40 -0800 Subject: [PATCH 01/43] Added printer --- Jakefile.js | 4 + src/compiler/comments.ts | 214 ++++ src/compiler/emitter.ts | 42 +- src/compiler/factory.ts | 20 +- src/compiler/printer.ts | 2421 +++++++++++++++++++++++++++++++++++ src/compiler/transformer.ts | 6 + src/compiler/tsconfig.json | 3 + src/compiler/types.ts | 1 + src/compiler/utilities.ts | 72 +- 9 files changed, 2730 insertions(+), 53 deletions(-) create mode 100644 src/compiler/comments.ts create mode 100644 src/compiler/printer.ts diff --git a/Jakefile.js b/Jakefile.js index 3dd9999aa8ae5..44cb1a61dbe65 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -44,6 +44,8 @@ var compilerSources = [ "visitor.ts", "transformer.ts", "sourcemap.ts", + "comments.ts", + "printer.ts", "declarationEmitter.ts", "emitter.ts", "program.ts", @@ -67,6 +69,8 @@ var servicesSources = [ "visitor.ts", "transformer.ts", "sourcemap.ts", + "comments.ts", + "printer.ts", "declarationEmitter.ts", "emitter.ts", "program.ts", diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts new file mode 100644 index 0000000000000..2b51e937fadd8 --- /dev/null +++ b/src/compiler/comments.ts @@ -0,0 +1,214 @@ +/// + +/* @internal */ +namespace ts { + export interface CommentWriter { + reset(): void; + setSourceFile(sourceFile: SourceFile): void; + getLeadingCommentsToEmit(node: TextRange): CommentRange[]; + getTrailingCommentsToEmit(node: TextRange): CommentRange[]; + emitDetachedComments(node: TextRange): void; + emitLeadingComments(node: TextRange, comments?: CommentRange[]): void; + emitTrailingComments(node: TextRange, comments?: CommentRange[]): void; + } + + export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter { + const compilerOptions = host.getCompilerOptions(); + const newLine = host.getNewLine(); + const { emitPos } = sourceMap; + + let currentSourceFile: SourceFile; + let currentText: string; + let currentLineMap: number[]; + let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[]; + + // This maps start->end for a comment range. See `hasConsumedCommentRange` and + // `consumeCommentRange` for usage. + let consumedCommentRanges: number[]; + let leadingCommentRangeNodeStarts: boolean[]; + let trailingCommentRangeNodeEnds: boolean[]; + + return compilerOptions.removeComments + ? createCommentRemovingWriter() + : createCommentPreservingWriter(); + + function createCommentRemovingWriter(): CommentWriter { + return { + reset, + setSourceFile, + getLeadingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; }, + getTrailingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; }, + emitDetachedComments, + emitLeadingComments(node: TextRange, comments?: CommentRange[]): void { }, + emitTrailingComments(node: TextRange, comments?: CommentRange[]): void { }, + }; + + function emitDetachedComments(node: TextRange): void { + emitDetachedCommentsAndUpdateCommentsInfo(node, /*removeComments*/ true); + } + } + + function createCommentPreservingWriter(): CommentWriter { + const noComments: CommentRange[] = []; + return { + reset, + setSourceFile, + getLeadingCommentsToEmit, + getTrailingCommentsToEmit, + emitDetachedComments, + emitLeadingComments, + emitTrailingComments, + }; + + function getLeadingCommentsToEmit(node: TextRange) { + if (nodeIsSynthesized(node)) { + return; + } + + if (!leadingCommentRangeNodeStarts[node.pos]) { + leadingCommentRangeNodeStarts[node.pos] = true; + const comments = hasDetachedComments(node.pos) + ? getLeadingCommentsWithoutDetachedComments() + : getLeadingCommentRanges(currentText, node.pos); + return consumeCommentRanges(comments); + } + + return noComments; + } + + function getTrailingCommentsToEmit(node: TextRange) { + if (nodeIsSynthesized(node)) { + return; + } + + if (!trailingCommentRangeNodeEnds[node.end]) { + trailingCommentRangeNodeEnds[node.end] = true; + const comments = getTrailingCommentRanges(currentText, node.end); + return consumeCommentRanges(comments); + } + + return noComments; + } + + function hasConsumedCommentRange(comment: CommentRange) { + return comment.end === consumedCommentRanges[comment.pos]; + } + + function consumeCommentRange(comment: CommentRange) { + if (!hasConsumedCommentRange(comment)) { + consumedCommentRanges[comment.pos] = comment.end; + return true; + } + + return false; + } + + function consumeCommentRanges(comments: CommentRange[]) { + let consumed: CommentRange[]; + if (comments) { + let commentsSkipped = 0; + let commentsConsumed = 0; + for (let i = 0; i < comments.length; i++) { + const comment = comments[i]; + if (consumeCommentRange(comment)) { + commentsConsumed++; + if (commentsSkipped !== 0) { + if (consumed === undefined) { + consumed = [comment]; + } + else { + consumed.push(comment); + } + } + } + else { + commentsSkipped++; + if (commentsConsumed !== 0 && consumed === undefined) { + consumed = comments.slice(0, i); + } + } + } + + if (commentsConsumed) { + return consumed || comments; + } + } + + return noComments; + } + + function emitLeadingComments(range: TextRange, leadingComments: CommentRange[] = getLeadingCommentsToEmit(range)) { + emitNewLineBeforeLeadingComments(currentLineMap, writer, range, leadingComments); + + // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space + emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); + } + + function emitTrailingComments(range: TextRange, trailingComments = getTrailingCommentsToEmit(range)) { + // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ + emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment); + } + + function emitDetachedComments(range: TextRange) { + emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false); + } + } + + function reset() { + currentSourceFile = undefined; + currentText = undefined; + currentLineMap = undefined; + detachedCommentsInfo = undefined; + consumedCommentRanges = undefined; + trailingCommentRangeNodeEnds = undefined; + leadingCommentRangeNodeStarts = undefined; + } + + function setSourceFile(sourceFile: SourceFile) { + currentSourceFile = sourceFile; + currentText = sourceFile.text; + currentLineMap = getLineStarts(sourceFile); + detachedCommentsInfo = undefined; + consumedCommentRanges = []; + leadingCommentRangeNodeStarts = []; + trailingCommentRangeNodeEnds = []; + } + + function hasDetachedComments(pos: number) { + return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos; + } + + function getLeadingCommentsWithoutDetachedComments() { + // get the leading comments from detachedPos + const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos; + const leadingComments = getLeadingCommentRanges(currentText, pos); + if (detachedCommentsInfo.length - 1) { + detachedCommentsInfo.pop(); + } + else { + detachedCommentsInfo = undefined; + } + + return leadingComments; + } + + function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange, removeComments: boolean) { + const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, node, newLine, removeComments); + + if (currentDetachedCommentInfo) { + if (detachedCommentsInfo) { + detachedCommentsInfo.push(currentDetachedCommentInfo); + } + else { + detachedCommentsInfo = [currentDetachedCommentInfo]; + } + } + } + + function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) { + emitPos(comment.pos); + writeCommentRange(text, lineMap, writer, comment, newLine); + emitPos(comment.end); + } + } +} \ No newline at end of file diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f0e32bdf96cad..4b1886fa8489a 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -895,7 +895,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } function emitLiteral(node: LiteralExpression | TemplateLiteralFragment) { - const text = getLiteralText(node); + const text = getLiteralText(node, currentSourceFile, languageVersion); if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { writer.writeLiteral(text); @@ -909,43 +909,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } } - function getLiteralText(node: LiteralExpression | TemplateLiteralFragment) { - // Any template literal or string literal with an extended escape - // (e.g. "\u{0067}") will need to be downleveled as a escaped string literal. - if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) { - return getQuotedEscapedLiteralText("\"", node.text, "\""); - } - - // If we don't need to downlevel and we can reach the original source text using - // the node's parent reference, then simply get the text as it was originally written. - if (node.parent) { - return getTextOfNodeFromSourceText(currentText, node); - } - - // If we can't reach the original source text, use the canonical form if it's a number, - // or an escaped quoted form of the original text if it's string-like. - switch (node.kind) { - case SyntaxKind.StringLiteral: - return getQuotedEscapedLiteralText("\"", node.text, "\""); - case SyntaxKind.NoSubstitutionTemplateLiteral: - return getQuotedEscapedLiteralText("`", node.text, "`"); - case SyntaxKind.TemplateHead: - return getQuotedEscapedLiteralText("`", node.text, "${"); - case SyntaxKind.TemplateMiddle: - return getQuotedEscapedLiteralText("}", node.text, "${"); - case SyntaxKind.TemplateTail: - return getQuotedEscapedLiteralText("}", node.text, "`"); - case SyntaxKind.NumericLiteral: - return node.text; - } - - Debug.fail(`Literal kind '${node.kind}' not accounted for.`); - } - - function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) { - return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote; - } - function emitDownlevelRawTemplateLiteral(node: LiteralExpression) { // 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. @@ -6590,7 +6553,8 @@ const _super = (function (geti, seti) { } const moduleName = getExternalModuleName(importNode); if (moduleName.kind === SyntaxKind.StringLiteral) { - return tryRenameExternalModule(moduleName) || getLiteralText(moduleName); + return tryRenameExternalModule(moduleName) + || getLiteralText(moduleName, currentSourceFile, languageVersion); } return undefined; diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index ff051ce477efa..0d225184a4ba5 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -144,14 +144,9 @@ namespace ts { export function createLiteral(value: string): StringLiteral; export function createLiteral(value: number): LiteralExpression; - export function createLiteral(value: string | number | boolean | void): PrimaryExpression; - export function createLiteral(value: string | number | boolean | void): T { - if (typeof value === "string") { - const node = createNode(SyntaxKind.StringLiteral); - node.text = value; - return node; - } - else if (typeof value === "number") { + export function createLiteral(value: string | number | boolean): PrimaryExpression; + export function createLiteral(value: string | number | boolean): T { + if (typeof value === "number") { const node = createNode(SyntaxKind.NumericLiteral); node.text = value.toString(); return node; @@ -159,8 +154,10 @@ namespace ts { else if (typeof value === "boolean") { return createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword); } - else if (value === null) { - return createNode(SyntaxKind.NullKeyword); + else { + const node = createNode(SyntaxKind.StringLiteral); + node.text = String(value); + return node; } } @@ -254,9 +251,6 @@ namespace ts { if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { return createLiteral(value); } - else if (value === null) { - return createLiteral(null); - } else { return value; } diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts new file mode 100644 index 0000000000000..150ddb6a918c2 --- /dev/null +++ b/src/compiler/printer.ts @@ -0,0 +1,2421 @@ +/// +/// +/// +/// +/// + +/* @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 + CountMask = 0x0FFFFFFF, // Temp variable counter + _i = 0x10000000, // Use/preference flag for '_i' + } + + // 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 { + // emit output for the __extends helper function + const extendsHelper = ` +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +};`; + + // emit output for the __decorate helper function + const decorateHelper = ` +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +};`; + + // emit output for the __metadata helper function + const metadataHelper = ` +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +};`; + + // emit output for the __param helper function + const paramHelper = ` +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +};`; + + // emit output for the __awaiter helper function + const awaiterHelper = ` +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +};`; + + // emit output for the __export helper function + const exportStarHelper = ` +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +}`; + + // emit output for the UMD helper function. + const umdHelper = ` +(function (dependencies, factory) { + if (typeof module === 'object' && typeof module.exports === 'object') { + var v = factory(require, exports); if (v !== undefined) module.exports = v; + } + else if (typeof define === 'function' && define.amd) { + define(dependencies, factory); + } +})`; + + const compilerOptions = host.getCompilerOptions(); + const languageVersion = getLanguageVersion(compilerOptions); + const moduleKind = getModuleKind(compilerOptions); + const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined; + const emitterDiagnostics = createDiagnosticCollection(); + + let emitSkipped = false; + const newLine = host.getNewLine(); + const printFile = createFilePrinter(); + forEachExpectedEmitFile(host, emitFile, targetSourceFile); + + return { + emitSkipped, + diagnostics: emitterDiagnostics.getDiagnostics(), + sourceMaps: sourceMapDataList + }; + + function emitFile({ jsFilePath, sourceMapFilePath, declarationFilePath}: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean) { + // Make sure not to write js file and source map file if any of them cannot be written + if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) { + printFile(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); + } + else { + emitSkipped = true; + } + + if (declarationFilePath) { + emitSkipped = writeDeclarationFile(declarationFilePath, sourceFiles, isBundledEmit, host, resolver, emitterDiagnostics) || emitSkipped; + } + } + + function createFilePrinter() { + const transformers = getTransformers(compilerOptions).concat(initializePrinter); + + const writer = createTextWriter(newLine); + const { write, writeTextOfNode, writeLine, increaseIndent, decreaseIndent } = writer; + + const sourceMap = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? createSourceMapWriter(host, writer) : getNullSourceMapWriter(); + const { emitStart, emitEnd, emitPos } = sourceMap; + + const comments = createCommentWriter(host, writer, sourceMap); + const { emitDetachedComments, emitLeadingComments, emitTrailingComments, getLeadingCommentsToEmit, getTrailingCommentsToEmit } = comments; + + let context: TransformationContext; + let startLexicalEnvironment: () => void; + let endLexicalEnvironment: () => Statement[]; + let getNodeEmitFlags: (node: Node) => NodeEmitFlags; + let expressionSubstitution: (node: Expression) => Expression; + let identifierSubstitution: (node: Identifier) => Identifier; + let isUniqueName: (name: string) => boolean; + let temporaryVariables: string[] = []; + let tempFlags: TempFlags; + let currentSourceFile: SourceFile; + let currentText: string; + let extendsEmitted: boolean; + let decorateEmitted: boolean; + let paramEmitted: boolean; + let awaiterEmitted: boolean; + let isOwnFileEmit: boolean; + + return doPrint; + + function doPrint(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { + sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); + isOwnFileEmit = !isBundledEmit; + + // Emit helpers from all the files + if (isBundledEmit && moduleKind) { + forEach(sourceFiles, emitEmitHelpers); + } + + // Transform and print the source files + transformFiles(resolver, host, sourceFiles, transformers); + + writeLine(); + + const sourceMappingURL = sourceMap.getSourceMappingURL(); + if (sourceMappingURL) { + write(`//# sourceMappingURL=${sourceMappingURL}`); + } + + // Write the source map + if (compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) { + writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), compilerOptions.emitBOM); + } + + // Record source map data for the test harness. + if (sourceMapDataList) { + sourceMapDataList.push(sourceMap.getSourceMapData()); + } + + // Write the output file + writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM); + + // Reset state + sourceMap.reset(); + comments.reset(); + writer.reset(); + + startLexicalEnvironment = undefined; + endLexicalEnvironment = undefined; + getNodeEmitFlags = undefined; + expressionSubstitution = undefined; + identifierSubstitution = undefined; + isUniqueName = undefined; + temporaryVariables = undefined; + tempFlags = 0; + currentSourceFile = undefined; + currentText = undefined; + extendsEmitted = false; + decorateEmitted = false; + paramEmitted = false; + awaiterEmitted = false; + isOwnFileEmit = false; + } + + function initializePrinter(_context: TransformationContext) { + context = _context; + startLexicalEnvironment = context.startLexicalEnvironment; + endLexicalEnvironment = context.endLexicalEnvironment; + getNodeEmitFlags = context.getNodeEmitFlags; + expressionSubstitution = context.expressionSubstitution; + identifierSubstitution = context.identifierSubstitution; + isUniqueName = context.isUniqueName; + return printSourceFile; + } + + function printSourceFile(node: SourceFile) { + currentSourceFile = node; + currentText = node.text; + sourceMap.setSourceFile(node); + comments.setSourceFile(node); + emitWorker(node); + return node; + } + + function emit(node: Node) { + if (node) { + const leadingComments = getLeadingCommentsToEmit(node); + const trailingComments = getTrailingCommentsToEmit(node); + emitLeadingComments(node, leadingComments); + emitStart(node); + emitWorker(node); + emitEnd(node); + emitTrailingComments(node, trailingComments); + } + } + + function emitWorker(node: Node) { + const kind = node.kind; + switch (kind) { + // Pseudo-literals + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + return emitLiteral(node); + + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node, identifierSubstitution); + + // Reserved words + case SyntaxKind.ConstKeyword: + case SyntaxKind.DefaultKeyword: + case SyntaxKind.ExportKeyword: + case SyntaxKind.VoidKeyword: + + // Strict mode reserved words + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.PublicKeyword: + case SyntaxKind.StaticKeyword: + + // Contextual keywords + case SyntaxKind.AbstractKeyword: + case SyntaxKind.AnyKeyword: + case SyntaxKind.AsyncKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.DeclareKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.ReadonlyKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.SymbolKeyword: + case SyntaxKind.GlobalKeyword: + return writeTokenNode(node); + + // Parse tree nodes + + // Names + case SyntaxKind.QualifiedName: + return emitQualifiedName(node); + case SyntaxKind.ComputedPropertyName: + return emitComputedPropertyName(node); + + // Signature elements + case SyntaxKind.TypeParameter: + return emitTypeParameter(node); + case SyntaxKind.Parameter: + return emitParameter(node); + case SyntaxKind.Decorator: + return emitDecorator(node); + + // Type members + case SyntaxKind.PropertySignature: + return emitPropertySignature(node); + case SyntaxKind.PropertyDeclaration: + return emitPropertyDeclaration(node); + case SyntaxKind.MethodSignature: + return emitMethodSignature(node); + case SyntaxKind.MethodDeclaration: + return emitMethodDeclaration(node); + case SyntaxKind.Constructor: + return emitConstructor(node); + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return emitAccessorDeclaration(node); + case SyntaxKind.CallSignature: + return emitCallSignature(node); + case SyntaxKind.ConstructSignature: + return emitConstructSignature(node); + case SyntaxKind.IndexSignature: + return emitIndexSignature(node); + + // Types + case SyntaxKind.TypePredicate: + return emitTypePredicate(node); + case SyntaxKind.TypeReference: + return emitTypeReference(node); + case SyntaxKind.FunctionType: + return emitFunctionType(node); + case SyntaxKind.ConstructorType: + return emitConstructorType(node); + case SyntaxKind.TypeQuery: + return emitTypeQuery(node); + case SyntaxKind.TypeLiteral: + return emitTypeLiteral(node); + case SyntaxKind.ArrayType: + return emitArrayType(node); + case SyntaxKind.TupleType: + return emitTupleType(node); + case SyntaxKind.UnionType: + return emitUnionType(node); + case SyntaxKind.IntersectionType: + return emitIntersectionType(node); + case SyntaxKind.ParenthesizedType: + return emitParenthesizedType(node); + case SyntaxKind.ExpressionWithTypeArguments: + return emitExpressionWithTypeArguments(node); + case SyntaxKind.ThisType: + return write("this"); + case SyntaxKind.StringLiteralType: + return emitLiteral(node); + + // Binding patterns + case SyntaxKind.ObjectBindingPattern: + return emitObjectBindingPattern(node); + case SyntaxKind.ArrayBindingPattern: + return emitArrayBindingPattern(node); + case SyntaxKind.BindingElement: + return emitBindingElement(node); + + // Misc + case SyntaxKind.TemplateSpan: + return emitTemplateSpan(node); + case SyntaxKind.SemicolonClassElement: + return write(";"); + + // Statements + case SyntaxKind.Block: + return emitBlock(node); + case SyntaxKind.VariableStatement: + return emitVariableStatement(node); + case SyntaxKind.EmptyStatement: + return write(";"); + case SyntaxKind.ExpressionStatement: + return emitExpressionStatement(node); + case SyntaxKind.IfStatement: + return emitIfStatement(node); + case SyntaxKind.DoStatement: + return emitDoStatement(node); + case SyntaxKind.WhileStatement: + return emitWhileStatement(node); + case SyntaxKind.ForStatement: + return emitForStatement(node); + case SyntaxKind.ForInStatement: + return emitForInStatement(node); + case SyntaxKind.ForOfStatement: + return emitForOfStatement(node); + case SyntaxKind.ContinueStatement: + return emitContinueStatement(node); + case SyntaxKind.BreakStatement: + return emitBreakStatement(node); + case SyntaxKind.ReturnStatement: + return emitReturnStatement(node); + case SyntaxKind.WithStatement: + return emitWithStatement(node); + case SyntaxKind.SwitchStatement: + return emitSwitchStatement(node); + case SyntaxKind.LabeledStatement: + return emitLabeledStatement(node); + case SyntaxKind.ThrowStatement: + return emitThrowStatement(node); + case SyntaxKind.TryStatement: + return emitTryStatement(node); + case SyntaxKind.DebuggerStatement: + return emitDebuggerStatement(node); + + // Declarations + case SyntaxKind.VariableDeclaration: + return emitVariableDeclaration(node); + case SyntaxKind.VariableDeclarationList: + return emitVariableDeclarationList(node); + case SyntaxKind.FunctionDeclaration: + return emitFunctionDeclaration(node); + case SyntaxKind.ClassDeclaration: + return emitClassDeclaration(node); + case SyntaxKind.InterfaceDeclaration: + return emitInterfaceDeclaration(node); + case SyntaxKind.TypeAliasDeclaration: + return emitTypeAliasDeclaration(node); + case SyntaxKind.EnumDeclaration: + return emitEnumDeclaration(node); + case SyntaxKind.ModuleDeclaration: + return emitModuleDeclaration(node); + case SyntaxKind.ModuleBlock: + return emitModuleBlock(node); + case SyntaxKind.CaseBlock: + return emitCaseBlock(node); + case SyntaxKind.ImportEqualsDeclaration: + return emitImportEqualsDeclaration(node); + case SyntaxKind.ImportDeclaration: + return emitImportDeclaration(node); + case SyntaxKind.ImportClause: + return emitImportClause(node); + case SyntaxKind.NamespaceImport: + return emitNamespaceImport(node); + case SyntaxKind.NamedImports: + return emitNamedImports(node); + case SyntaxKind.ImportSpecifier: + return emitImportSpecifier(node); + case SyntaxKind.ExportAssignment: + return emitExportAssignment(node); + case SyntaxKind.ExportDeclaration: + return emitExportDeclaration(node); + case SyntaxKind.NamedExports: + return emitNamedExports(node); + case SyntaxKind.ExportSpecifier: + return emitExportSpecifier(node); + case SyntaxKind.MissingDeclaration: + return; + + // Module references + case SyntaxKind.ExternalModuleReference: + return emitExternalModuleReference(node); + + // JSX (non-expression) + case SyntaxKind.JsxText: + return emitJsxText(node); + case SyntaxKind.JsxClosingElement: + return emitJsxClosingElement(node); + case SyntaxKind.JsxAttribute: + return emitJsxAttribute(node); + case SyntaxKind.JsxSpreadAttribute: + return emitJsxSpreadAttribute(node); + + // Clauses + case SyntaxKind.CaseClause: + return emitCaseClause(node); + case SyntaxKind.DefaultClause: + return emitDefaultClause(node); + case SyntaxKind.HeritageClause: + return emitHeritageClause(node); + case SyntaxKind.CatchClause: + return emitCatchClause(node); + + // Property assignments + case SyntaxKind.PropertyAssignment: + return emitPropertyAssignment(node); + case SyntaxKind.ShorthandPropertyAssignment: + return emitShorthandPropertyAssignment(node); + + // Enum + case SyntaxKind.EnumMember: + return emitEnumMember(node); + + // Top-level nodes + case SyntaxKind.SourceFile: + return emitSourceFile(node); + + // JSDoc nodes (ignored) + } + + if (isExpressionKind(kind)) { + return emitExpressionWorker(node); + } + } + + function emitExpression(node: Expression) { + if (node) { + const leadingComments = getLeadingCommentsToEmit(node); + const trailingComments = getTrailingCommentsToEmit(node); + emitLeadingComments(node, leadingComments); + emitStart(node); + emitExpressionWorker(node); + emitEnd(node); + emitTrailingComments(node, trailingComments); + } + } + + function emitExpressionWorker(node: Node) { + const kind = node.kind; + switch (kind) { + // Literals + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + return emitLiteral(node); + + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node, expressionSubstitution); + + // Reserved words + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.TrueKeyword: + return writeTokenNode(node); + case SyntaxKind.ThisKeyword: + return emitThisKeyword(node); + + // Expressions + case SyntaxKind.ArrayLiteralExpression: + return emitArrayLiteralExpression(node); + case SyntaxKind.ObjectLiteralExpression: + return emitObjectLiteralExpression(node); + case SyntaxKind.PropertyAccessExpression: + return emitPropertyAccessExpression(node); + case SyntaxKind.ElementAccessExpression: + return emitElementAccessExpression(node); + case SyntaxKind.CallExpression: + return emitCallExpression(node); + case SyntaxKind.NewExpression: + return emitNewExpression(node); + case SyntaxKind.TaggedTemplateExpression: + return emitTaggedTemplateExpression(node); + case SyntaxKind.TypeAssertionExpression: + return emitTypeAssertionExpression(node); + case SyntaxKind.ParenthesizedExpression: + return emitParenthesizedExpression(node); + case SyntaxKind.FunctionExpression: + return emitFunctionExpression(node); + case SyntaxKind.ArrowFunction: + return emitArrowFunction(node); + case SyntaxKind.DeleteExpression: + return emitDeleteExpression(node); + case SyntaxKind.TypeOfExpression: + return emitTypeOfExpression(node); + case SyntaxKind.VoidExpression: + return emitVoidExpression(node); + case SyntaxKind.AwaitExpression: + return emitAwaitExpression(node); + case SyntaxKind.PrefixUnaryExpression: + return emitPrefixUnaryExpression(node); + case SyntaxKind.PostfixUnaryExpression: + return emitPostfixUnaryExpression(node); + case SyntaxKind.BinaryExpression: + return emitBinaryExpression(node); + case SyntaxKind.ConditionalExpression: + return emitConditionalExpression(node); + case SyntaxKind.TemplateExpression: + return emitTemplateExpression(node); + case SyntaxKind.YieldExpression: + return emitYieldExpression(node); + case SyntaxKind.SpreadElementExpression: + return emitSpreadElementExpression(node); + case SyntaxKind.ClassExpression: + return emitClassExpression(node); + case SyntaxKind.OmittedExpression: + return; + case SyntaxKind.AsExpression: + return emitAsExpression(node); + + // JSX + case SyntaxKind.JsxElement: + return emitJsxElement(node); + case SyntaxKind.JsxSelfClosingElement: + return emitJsxSelfClosingElement(node); + case SyntaxKind.JsxOpeningElement: + return emitJsxOpeningElement(node); + case SyntaxKind.JsxExpression: + return emitJsxExpression(node); + } + } + + // + // Literals/Pseudo-literals + // + + // SyntaxKind.NumericLiteral + // SyntaxKind.StringLiteral + // SyntaxKind.RegularExpressionLiteral + // SyntaxKind.NoSubstitutionTemplateLiteral + // SyntaxKind.TemplateHead + // SyntaxKind.TemplateMiddle + // SyntaxKind.TemplateTail + function emitLiteral(node: LiteralLikeNode) { + const text = getLiteralText(node, currentSourceFile, languageVersion); + if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) + && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { + writer.writeLiteral(text); + } + else { + write(text); + } + } + + // + // Identifiers + // + + function emitIdentifier(node: Identifier, substitution: (node: Node) => Node) { + if (tryEmitSubstitute(node, substitution)) { + return; + } + else if (node.text === undefined) { + // Emit a temporary variable name for this node. + const nodeId = getOriginalNodeId(node); + const text = temporaryVariables[nodeId] || (temporaryVariables[nodeId] = makeTempVariableName(tempKindToFlags(node.tempKind))); + write(text); + } + else if (nodeIsSynthesized(node)) { + if (getNodeEmitFlags(node) & NodeEmitFlags.UMDDefine) { + writeLines(umdHelper); + } + else { + write(node.text); + } + } + else { + writeTextOfNode(currentText, node); + } + } + + function emitThisKeyword(node: PrimaryExpression) { + if (tryEmitSubstitute(node, expressionSubstitution)) { + return; + } + else { + writeTokenNode(node); + } + } + + // + // Names + // + + function emitQualifiedName(node: QualifiedName) { + emitEntityName(node.left); + write("."); + emit(node.right); + } + + function emitEntityName(node: EntityName) { + if (node.kind === SyntaxKind.Identifier) { + emitExpression(node); + } + else { + emit(node); + } + } + + function emitComputedPropertyName(node: ComputedPropertyName) { + write("["); + emitExpression(node.expression); + write("]"); + } + + // + // Signature elements + // + + function emitTypeParameter(node: TypeParameterDeclaration) { + emit(node.name); + emitWithPrefix(" extends ", node.constraint); + } + + function emitParameter(node: ParameterDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + writeIfPresent(node.dotDotDotToken, "..."); + emit(node.name); + writeIfPresent(node.questionToken, "?"); + emitExpressionWithPrefix(" = ", node.initializer); + emitWithPrefix(": ", node.type); + } + + function emitDecorator(decorator: Decorator) { + write("@"); + emitExpression(decorator.expression); + } + + // + // Type members + // + + function emitPropertySignature(node: PropertySignature) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + emit(node.name); + writeIfPresent(node.questionToken, "?"); + emitWithPrefix(": ", node.type); + write(";"); + } + + function emitPropertyDeclaration(node: PropertyDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + emit(node.name); + emitWithPrefix(": ", node.type); + emitExpressionWithPrefix(" = ", node.initializer); + write(";"); + } + + function emitMethodSignature(node: MethodSignature) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + emit(node.name); + writeIfPresent(node.questionToken, "?"); + emitTypeParameters(node, node.typeParameters); + emitParameters(node, node.parameters); + emitWithPrefix(": ", node.type); + write(";"); + } + + function emitMethodDeclaration(node: MethodDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + writeIfPresent(node.asteriskToken, "*"); + emit(node.name); + emitSignatureAndBody(node); + } + + function emitConstructor(node: ConstructorDeclaration) { + emitModifiers(node, node.modifiers); + write("constructor"); + emitSignatureAndBody(node); + } + + function emitAccessorDeclaration(node: AccessorDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); + emit(node.name); + emitSignatureAndBody(node); + } + + function emitCallSignature(node: CallSignatureDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + emitTypeParameters(node, node.typeParameters); + emitParameters(node, node.parameters); + emitWithPrefix(": ", node.type); + write(";"); + } + + function emitConstructSignature(node: ConstructSignatureDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write("new "); + emitTypeParameters(node, node.typeParameters); + emitParameters(node, node.parameters); + emitWithPrefix(": ", node.type); + write(";"); + } + + function emitIndexSignature(node: IndexSignatureDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + emitParametersForIndexSignature(node, node.parameters); + emitWithPrefix(": ", node.type); + write(";"); + } + + // + // Types + // + + function emitTypePredicate(node: TypePredicateNode) { + emit(node.parameterName); + write(" is "); + emit(node.type); + } + + function emitTypeReference(node: TypeReferenceNode) { + emit(node.typeName); + emitTypeArguments(node, node.typeArguments); + } + + function emitFunctionType(node: FunctionTypeNode) { + emitTypeParameters(node, node.typeParameters); + emitParametersForArrow(node, node.parameters); + write(" => "); + emit(node.type); + } + + function emitConstructorType(node: ConstructorTypeNode) { + write("new "); + emitTypeParameters(node, node.typeParameters); + emitParametersForArrow(node, node.parameters); + write(" => "); + emit(node.type); + } + + function emitTypeQuery(node: TypeQueryNode) { + write("typeof "); + emit(node.exprName); + } + + function emitTypeLiteral(node: TypeLiteralNode) { + write("{"); + emitList(node, node.members, ListFormat.TypeLiteralMembers); + write("}"); + } + + function emitArrayType(node: ArrayTypeNode) { + emit(node.elementType); + write("[]"); + } + + function emitTupleType(node: TupleTypeNode) { + write("["); + emitList(node, node.elementTypes, ListFormat.TupleTypeElements); + write("]"); + } + + function emitUnionType(node: UnionTypeNode) { + emitList(node, node.types, ListFormat.UnionTypeConstituents); + } + + function emitIntersectionType(node: IntersectionTypeNode) { + emitList(node, node.types, ListFormat.IntersectionTypeConstituents); + } + + function emitParenthesizedType(node: ParenthesizedTypeNode) { + write("("); + emit(node.type); + write(")"); + } + + // + // Binding patterns + // + + function emitObjectBindingPattern(node: ObjectBindingPattern) { + const elements = node.elements; + if (elements.length === 0) { + write("{}"); + } + else { + write("{"); + emitList(node, elements, ListFormat.ObjectBindingPatternElements); + write("}"); + } + } + + function emitArrayBindingPattern(node: ArrayBindingPattern) { + const elements = node.elements; + if (elements.length === 0) { + write("[]"); + } + else { + write("["); + emitList(node, node.elements, ListFormat.ArrayBindingPatternElements); + write("]"); + } + } + + function emitBindingElement(node: BindingElement) { + emitWithSuffix(node.propertyName, ": "); + writeIfPresent(node.dotDotDotToken, "..."); + emit(node.name); + emitExpressionWithPrefix(" = ", node.initializer); + } + + // + // Expressions + // + + function emitArrayLiteralExpression(node: ArrayLiteralExpression) { + const elements = node.elements; + if (elements.length === 0) { + write("[]"); + } + else { + const preferNewLine = getNodeEmitFlags(node) & NodeEmitFlags.MultiLine ? ListFormat.PreferNewLine : ListFormat.None; + emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine); + } + } + + function emitObjectLiteralExpression(node: ObjectLiteralExpression) { + const properties = node.properties; + if (properties.length === 0) { + write("{}"); + } + else { + const preferNewLine = getNodeEmitFlags(node) & NodeEmitFlags.MultiLine ? ListFormat.PreferNewLine : ListFormat.None; + const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None; + emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); + } + } + + function emitPropertyAccessExpression(node: PropertyAccessExpression) { + if (tryEmitConstantValue(node)) { + return; + } + + const indentBeforeDot = needsIndentation(node, node.expression, node.dotToken); + const indentAfterDot = needsIndentation(node, node.dotToken, node.name); + const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); + + emitExpression(node.expression); + increaseIndentIf(indentBeforeDot); + write(shouldEmitDotDot ? ".." : "."); + increaseIndentIf(indentAfterDot); + emit(node.name); + decreaseIndentIf(indentBeforeDot, indentAfterDot); + } + + // 1..toString is a valid property access, emit a dot after the literal + // Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal + function needsDotDotForPropertyAccess(expression: Expression) { + if (expression.kind === SyntaxKind.NumericLiteral) { + // check if numeric literal was originally written with a dot + const text = getLiteralText(expression, currentSourceFile, languageVersion); + return text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; + } + else { + // check if constant enum value is integer + const constantValue = tryGetConstEnumValue(expression); + // isFinite handles cases when constantValue is undefined + return isFinite(constantValue) && Math.floor(constantValue) === constantValue; + } + } + + function emitElementAccessExpression(node: ElementAccessExpression) { + if (tryEmitConstantValue(node)) { + return; + } + + emitExpression(node.expression); + write("["); + emitExpression(node.argumentExpression); + write("]"); + } + + function emitCallExpression(node: CallExpression) { + emitExpression(node.expression); + emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); + } + + function emitNewExpression(node: NewExpression) { + write("new "); + emitExpression(node.expression); + if (node.arguments) { + emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments); + } + } + + function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { + emitExpression(node.tag); + emitExpression(node.template); + } + + function emitTypeAssertionExpression(node: TypeAssertion) { + if (node.type) { + write("<"); + emit(node.type); + write(">"); + } + + emitExpression(node.expression); + } + + function emitParenthesizedExpression(node: ParenthesizedExpression) { + write("("); + emitExpression(node.expression); + write(")"); + } + + function emitFunctionExpression(node: FunctionExpression) { + emitFunctionDeclarationOrExpression(node); + } + + function emitArrowFunction(node: ArrowFunction) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + const body = node.body; + if (isBlock(body)) { + const savedTempFlags = tempFlags; + tempFlags = 0; + startLexicalEnvironment(); + emitArrowFunctionHead(node); + write(" {"); + + const startingLine = writer.getLine(); + emitBlockFunctionBody(node, body); + + const endingLine = writer.getLine(); + emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); + tempFlags = savedTempFlags; + write("}"); + } + else { + emitArrowFunctionHead(node); + write(" "); + emitExpression(body); + } + } + + function emitArrowFunctionHead(node: ArrowFunction) { + emitTypeParameters(node, node.typeParameters); + emitParametersForArrow(node, node.parameters); + emitWithPrefix(": ", node.type); + write(" =>"); + } + + function emitDeleteExpression(node: DeleteExpression) { + write("delete "); + emitExpression(node.expression); + } + + function emitTypeOfExpression(node: TypeOfExpression) { + write("typeof "); + emitExpression(node.expression); + } + + function emitVoidExpression(node: VoidExpression) { + write("void "); + emitExpression(node.expression); + } + + function emitAwaitExpression(node: AwaitExpression) { + write("await "); + emitExpression(node.expression); + } + + function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { + writeToken(node.operator); + if (shouldEmitWhitespaceBeforeOperand(node)) { + write(" "); + } + emitExpression(node.operand); + } + + function shouldEmitWhitespaceBeforeOperand(node: PrefixUnaryExpression) { + // In some cases, we need to emit a space between the operator and the operand. One obvious case + // is when the operator is an identifier, like delete or typeof. We also need to do this for plus + // and minus expressions in certain cases. Specifically, consider the following two cases (parens + // are just for clarity of exposition, and not part of the source code): + // + // (+(+1)) + // (+(++1)) + // + // We need to emit a space in both cases. In the first case, the absence of a space will make + // the resulting expression a prefix increment operation. And in the second, it will make the resulting + // expression a prefix increment whose operand is a plus expression - (++(+x)) + // The same is true of minus of course. + const operand = node.operand; + return operand.kind === SyntaxKind.PrefixUnaryExpression + && ((node.operator === SyntaxKind.PlusToken && ((operand).operator === SyntaxKind.PlusToken || (operand).operator === SyntaxKind.PlusPlusToken)) + || (node.operator === SyntaxKind.MinusToken && ((operand).operator === SyntaxKind.MinusToken || (operand).operator === SyntaxKind.MinusMinusToken))); + } + + function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { + if (tryEmitSubstitute(node, expressionSubstitution)) { + return; + } + + emitExpression(node.operand); + writeToken(node.operator); + } + + function emitBinaryExpression(node: BinaryExpression) { + if (tryEmitSubstitute(node, expressionSubstitution)) { + return; + } + + const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; + const indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); + const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); + + emitExpression(node.left); + increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); + writeTokenNode(node.operatorToken); + increaseIndentIf(indentAfterOperator, " "); + emitExpression(node.right); + decreaseIndentIf(indentBeforeOperator, indentAfterOperator); + } + + function emitConditionalExpression(node: ConditionalExpression) { + const indentBeforeQuestion = needsIndentation(node, node.condition, node.questionToken); + const indentAfterQuestion = needsIndentation(node, node.questionToken, node.whenTrue); + const indentBeforeColon = needsIndentation(node, node.whenTrue, node.colonToken); + const indentAfterColon = needsIndentation(node, node.colonToken, node.whenFalse); + + emitExpression(node.condition); + increaseIndentIf(indentBeforeQuestion, " "); + write("?"); + increaseIndentIf(indentAfterQuestion, " "); + emitExpression(node.whenTrue); + decreaseIndentIf(indentBeforeQuestion, indentAfterQuestion); + + increaseIndentIf(indentBeforeColon, " "); + write(":"); + increaseIndentIf(indentAfterColon, " "); + emitExpression(node.whenFalse); + decreaseIndentIf(indentBeforeColon, indentAfterColon); + } + + function emitTemplateExpression(node: TemplateExpression) { + emit(node.head); + emitList(node, node.templateSpans, ListFormat.TemplateExpressionSpans); + } + + function emitYieldExpression(node: YieldExpression) { + write(node.asteriskToken ? "yield*" : "yield"); + emitExpressionWithPrefix(" ", node.expression); + } + + function emitSpreadElementExpression(node: SpreadElementExpression) { + write("..."); + emitExpression(node.expression); + } + + function emitClassExpression(node: ClassExpression) { + emitClassDeclarationOrExpression(node); + } + + function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { + emitStart(node); + emitExpression(node.expression); + emitTypeArguments(node, node.typeArguments); + emitEnd(node); + } + + function emitAsExpression(node: AsExpression) { + emitExpression(node.expression); + if (node.type) { + write(" as "); + emit(node.type); + } + } + + // + // Misc + // + + function emitTemplateSpan(node: TemplateSpan) { + emitExpression(node.expression); + emit(node.literal); + } + + // + // Statements + // + + function emitBlock(node: Block, format?: ListFormat) { + if (isSingleLineEmptyBlock(node)) { + write("{ }"); + } + else { + write("{"); + emitBlockStatements(node); + write("}"); + } + } + + function emitBlockStatements(node: Block) { + if (getNodeEmitFlags(node) & NodeEmitFlags.SingleLine) { + emitList(node, node.statements, ListFormat.SingleLineBlockStatements); + } + else { + emitList(node, node.statements, ListFormat.MultiLineBlockStatements); + } + } + + function emitVariableStatement(node: VariableStatement) { + emitModifiers(node, node.modifiers); + emit(node.declarationList); + write(";"); + } + + function emitExpressionStatement(node: ExpressionStatement) { + emitExpression(node.expression); + write(";"); + } + + function emitIfStatement(node: IfStatement) { + write("if ("); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node.thenStatement); + if (node.elseStatement) { + writeLine(); + write("else"); + if (node.elseStatement.kind === SyntaxKind.IfStatement) { + write(" "); + emit(node.elseStatement); + } + else { + emitEmbeddedStatement(node.elseStatement); + } + } + } + + function emitDoStatement(node: DoStatement) { + write("do"); + emitEmbeddedStatement(node.statement); + if (isBlock(node.statement)) { + write(" "); + } + else { + writeLine(); + } + + write("while ("); + emitExpression(node.expression); + write(");"); + } + + function emitWhileStatement(node: WhileStatement) { + write("while ("); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitForStatement(node: ForStatement) { + write("for ("); + emitForBinding(node.initializer); + write(";"); + emitExpressionWithPrefix(" ", node.condition); + write(";"); + emitExpressionWithPrefix(" ", node.incrementor); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitForInStatement(node: ForInStatement) { + write("for ("); + emitForBinding(node.initializer); + write(" in "); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitForOfStatement(node: ForOfStatement) { + write("for ("); + emitForBinding(node.initializer); + write(" of "); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitForBinding(node: VariableDeclarationList | Expression) { + if (node !== undefined) { + if (node.kind === SyntaxKind.VariableDeclarationList) { + emit(node); + } + else { + emitExpression(node); + } + } + } + + function emitContinueStatement(node: ContinueStatement) { + write("continue"); + emitWithPrefix(" ", node.label); + write(";"); + } + + function emitBreakStatement(node: BreakStatement) { + write("break"); + emitWithPrefix(" ", node.label); + write(";"); + } + + function emitReturnStatement(node: ReturnStatement) { + write("return"); + emitExpressionWithPrefix(" ", node.expression); + write(";"); + } + + function emitWithStatement(node: WithStatement) { + write("with ("); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitSwitchStatement(node: SwitchStatement) { + write("switch ("); + emitExpression(node.expression); + write(") "); + emit(node.caseBlock); + } + + function emitLabeledStatement(node: LabeledStatement) { + emit(node.label); + write(": "); + emit(node.statement); + } + + function emitThrowStatement(node: ThrowStatement) { + write("throw"); + emitExpressionWithPrefix(" ", node.expression); + write(";"); + } + + function emitTryStatement(node: TryStatement) { + write("try "); + emit(node.tryBlock); + emit(node.catchClause); + if (node.finallyBlock) { + writeLine(); + write("finally "); + emit(node.finallyBlock); + } + } + + function emitDebuggerStatement(node: DebuggerStatement) { + write("debugger;"); + } + + // + // Declarations + // + + function emitVariableDeclaration(node: VariableDeclaration) { + emit(node.name); + emitExpressionWithPrefix(" = ", node.initializer); + } + + function emitVariableDeclarationList(node: VariableDeclarationList) { + write(isLet(node) ? "let " : isConst(node) ? "const " : "var "); + emitList(node, node.declarations, ListFormat.VariableDeclarationList); + } + + function emitFunctionDeclaration(node: FunctionDeclaration) { + emitFunctionDeclarationOrExpression(node); + } + + function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write(node.asteriskToken ? "function* " : "function "); + emit(node.name); + emitSignatureAndBody(node); + } + + function emitSignatureAndBody(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) { + const body = node.body; + if (body) { + const savedTempFlags = tempFlags; + tempFlags = 0; + startLexicalEnvironment(); + emitSignatureHead(node); + write(" {"); + + const startingLine = writer.getLine(); + emitBlockFunctionBody(node, body); + + const endingLine = writer.getLine(); + emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); + write("}"); + tempFlags = savedTempFlags; + } + else { + emitSignatureHead(node); + write(";"); + } + + } + + function emitSignatureHead(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) { + emitTypeParameters(node, node.typeParameters); + emitParameters(node, node.parameters); + emitWithPrefix(": ", node.type); + } + + function shouldEmitBlockFunctionBodyOnSingleLine(parentNode: Node, body: Block) { + const originalNode = getOriginalNode(parentNode); + if (isFunctionLike(originalNode) && !nodeIsSynthesized(originalNode) && rangeEndIsOnSameLineAsRangeStart(originalNode.body, originalNode.body)) { + for (const statement of body.statements) { + if (synthesizedNodeStartsOnNewLine(statement)) { + return false; + } + } + + if (originalNode.kind === SyntaxKind.ArrowFunction && !rangeEndIsOnSameLineAsRangeStart((originalNode).equalsGreaterThanToken, originalNode.body)) { + return false; + } + + return true; + } + + return false; + } + + function emitBlockFunctionBody(parentNode: Node, body: Block) { + // Emit all the prologue directives (like "use strict"). + const statements = body.statements; + const statementOffset = emitPrologueDirectives(statements, /*startWithNewLine*/ true, /*indented*/ true); + const helpersEmitted = emitHelpers(body); + if (statementOffset === 0 && !helpersEmitted && shouldEmitBlockFunctionBodyOnSingleLine(parentNode, body)) { + emitList(body, statements, ListFormat.SingleLineFunctionBodyStatements); + } + else { + emitList(body, statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); + } + } + + function emitClassDeclaration(node: ClassDeclaration) { + emitClassDeclarationOrExpression(node); + } + + function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write("class"); + emitWithPrefix(" ", node.name); + emitTypeParameters(node, node.typeParameters); + emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); + + const savedTempFlags = tempFlags; + tempFlags = 0; + + write(" {"); + emitList(node, node.members, ListFormat.ClassMembers); + write("}"); + + tempFlags = savedTempFlags; + } + + function emitInterfaceDeclaration(node: InterfaceDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write("interface "); + emit(node.name); + emitTypeParameters(node, node.typeParameters); + emitList(node, node.heritageClauses, ListFormat.SingleLine); + write(" {"); + emitList(node, node.members, ListFormat.InterfaceMembers); + write("}"); + } + + function emitTypeAliasDeclaration(node: TypeAliasDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write("type "); + emit(node.name); + emitTypeParameters(node, node.typeParameters); + write(" = "); + emit(node.type); + write(";"); + } + + function emitEnumDeclaration(node: EnumDeclaration) { + emitModifiers(node, node.modifiers); + write("enum "); + emit(node.name); + + const savedTempFlags = tempFlags; + tempFlags = 0; + + write(" {"); + emitList(node, node.members, ListFormat.EnumMembers); + write("}"); + tempFlags = savedTempFlags; + } + + function emitModuleDeclaration(node: ModuleDeclaration) { + emitModifiers(node, node.modifiers); + write(node.flags & NodeFlags.Namespace ? "namespace " : "module "); + emit(node.name); + + let body = node.body; + while (body.kind === SyntaxKind.ModuleDeclaration) { + write("."); + emit((body).name); + body = (body).body; + } + + write(" "); + emit(body); + } + + function emitModuleBlock(node: ModuleBlock) { + if (isSingleLineEmptyBlock(node)) { + write("{ }"); + } + else { + const savedTempFlags = tempFlags; + tempFlags = 0; + startLexicalEnvironment(); + write("{"); + increaseIndent(); + + const startingLine = writer.getLine(); + emitBlockStatements(node); + + const endingLine = writer.getLine(); + emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); + write("}"); + tempFlags = savedTempFlags; + } + } + + function emitCaseBlock(node: CaseBlock) { + write("{"); + emitList(node, node.clauses, ListFormat.CaseBlockClauses); + write("}"); + } + + function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) { + emitModifiers(node, node.modifiers); + write("import "); + emit(node.name); + write(" = "); + emitModuleReference(node.moduleReference); + write(";"); + } + + function emitModuleReference(node: ModuleReference) { + if (node.kind === SyntaxKind.Identifier) { + emitExpression(node); + } + else { + emit(node); + } + } + + function emitImportDeclaration(node: ImportDeclaration) { + emitModifiers(node, node.modifiers); + write("import "); + emit(node.importClause); + emitExpression(node.moduleSpecifier); + write(";"); + } + + function emitImportClause(node: ImportClause) { + emitStart(node); + emit(node.name); + if (node.name && node.namedBindings) { + write(", "); + } + emit(node.namedBindings); + emitEnd(node); + write(" from "); + } + + function emitNamespaceImport(node: NamespaceImport) { + emitStart(node); + write("* as "); + emit(node.name); + emitEnd(node); + } + + function emitNamedImports(node: NamedImports) { + emitNamedImportsOrExports(node); + } + + function emitImportSpecifier(node: ImportSpecifier) { + emitImportOrExportSpecifier(node); + } + + function emitExportAssignment(node: ExportAssignment) { + write(node.isExportEquals ? "export = " : "export default "); + emitExpression(node.expression); + write(";"); + } + + function emitExportDeclaration(node: ExportDeclaration) { + write("export "); + if (node.exportClause) { + emit(node.exportClause); + write(" from "); + } + else { + write("* from "); + } + emitExpression(node.moduleSpecifier); + write(";"); + } + + function emitNamedExports(node: NamedExports) { + emitNamedImportsOrExports(node); + } + + function emitExportSpecifier(node: ExportSpecifier) { + emitImportOrExportSpecifier(node); + } + + function emitNamedImportsOrExports(node: NamedImportsOrExports) { + write("{"); + emitList(node, node.elements, ListFormat.NamedImportsOrExportsElements); + write("}"); + } + + function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) { + if (node.propertyName) { + emit(node.propertyName); + write(" as "); + } + + emit(node.name); + } + + // + // Module references + // + + function emitExternalModuleReference(node: ExternalModuleReference) { + write("require("); + emitExpression(node.expression); + write(")"); + } + + // + // JSX + // + + function emitJsxElement(node: JsxElement) { + emit(node.openingElement); + emitList(node, node.children, ListFormat.JsxElementChildren); + emit(node.closingElement); + } + + function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { + write("<"); + emit(node.tagName); + write(" "); + emitList(node, node.attributes, ListFormat.JsxElementAttributes); + write("/>"); + } + + function emitJsxOpeningElement(node: JsxOpeningElement) { + write("<"); + emit(node.tagName); + writeIfAny(node.attributes, " "); + emitList(node, node.attributes, ListFormat.JsxElementAttributes); + write(">"); + } + + function emitJsxText(node: JsxText) { + writer.writeLiteral(getTextOfNode(node, /*includeTrivia*/ true)); + } + + function emitJsxClosingElement(node: JsxClosingElement) { + write(""); + } + + function emitJsxAttribute(node: JsxAttribute) { + emit(node.name); + emitWithPrefix("=", node.initializer); + } + + function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { + write("{..."); + emitExpression(node.expression); + write("}"); + } + + function emitJsxExpression(node: JsxExpression) { + write("{"); + emitExpression(node.expression); + write("}"); + } + + // + // Clauses + // + + function emitCaseClause(node: CaseClause) { + write("case "); + emitExpression(node.expression); + write(":"); + emitCaseOrDefaultClauseStatements(node, node.statements); + } + + function emitDefaultClause(node: DefaultClause) { + write("default:"); + emitCaseOrDefaultClauseStatements(node, node.statements); + } + + function emitCaseOrDefaultClauseStatements(parentNode: Node, statements: NodeArray) { + if (statements.length === 1 && rangeStartPositionsAreOnSameLine(parentNode, statements[0])) { + write(" "); + emit(statements[0]); + } + else { + emitList(parentNode, statements, ListFormat.CaseOrDefaultClauseStatements); + } + } + + function emitHeritageClause(node: HeritageClause) { + emitStart(node); + writeToken(node.token); + write(" "); + emitList(node, node.types, ListFormat.HeritageClauseTypes); + emitEnd(node); + } + + function emitCatchClause(node: CatchClause) { + writeLine(); + write("catch ("); + emit(node.variableDeclaration); + write(") "); + emit(node.block); + } + + // + // Property assignments + // + + function emitPropertyAssignment(node: PropertyAssignment) { + emit(node.name); + write(": "); + // // This is to ensure that we emit comment in the following case: + // // For example: + // // obj = { + // // id: /*comment1*/ ()=>void + // // } + // // "comment1" is not considered to be leading comment for node.initializer + // // but rather a trailing comment on the previous node. + // emitTrailingCommentsOfPosition(node.initializer.pos); + emitExpression(node.initializer); + } + + function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { + emit(node.name); + } + + // + // Enum + // + + function emitEnumMember(node: EnumMember) { + emit(node.name); + emitExpressionWithPrefix(" = ", node.initializer); + } + + // + // Top-level nodes + // + + function emitSourceFile(node: SourceFile) { + writeLine(); + emitShebang(); + emitDetachedComments(node); + + const statements = node.statements; + const statementOffset = emitPrologueDirectives(statements); + if (getNodeEmitFlags(node) & NodeEmitFlags.NoLexicalEnvironment) { + emitHelpers(node); + emitList(node, statements, ListFormat.MultiLine, statementOffset); + } + else { + const savedTempFlags = tempFlags; + tempFlags = 0; + startLexicalEnvironment(); + emitHelpers(node); + emitList(node, statements, ListFormat.MultiLine, statementOffset); + emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ true); + tempFlags = savedTempFlags; + } + + emitLeadingComments(node.endOfFileToken); + } + + function emitLexicalEnvironment(declarations: Statement[], newLine: boolean) { + if (declarations && declarations.length > 0) { + for (const node of declarations) { + if (newLine) { + writeLine(); + } + else { + write(" "); + } + + emit(node); + } + + if (newLine) { + writeLine(); + } + else { + write(" "); + } + } + } + + function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean, indented?: boolean) { + increaseIndentIf(indented); + for (let i = 0; i < statements.length; i++) { + if (isPrologueDirective(statements[i])) { + if (startWithNewLine || i > 0) { + writeLine(); + } + emit(statements[i]); + } + else { + // return index of the first non prologue directive + decreaseIndentIf(indented); + return i; + } + } + + decreaseIndentIf(indented); + return statements.length; + } + + function emitHelpers(node: Node) { + const emitFlags = getNodeEmitFlags(node); + let helpersEmitted = false; + if (emitFlags & NodeEmitFlags.EmitHelpers) { + helpersEmitted = emitEmitHelpers(currentSourceFile); + } + + if (emitFlags & NodeEmitFlags.EmitExportStar) { + emitExportStarHelper(); + helpersEmitted = true; + } + + return helpersEmitted; + } + + function emitEmitHelpers(node: SourceFile) { + let helpersEmitted = false; + + // Only emit helpers if the user did not say otherwise. + if (!compilerOptions.noEmitHelpers) { + // Only Emit __extends function when target ES5. + // For target ES6 and above, we can emit classDeclaration as is. + if ((languageVersion < ScriptTarget.ES6) && (!extendsEmitted && node.flags & NodeFlags.HasClassExtends)) { + writeLines(extendsHelper); + extendsEmitted = true; + helpersEmitted = true; + } + + if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) { + writeLines(decorateHelper); + if (compilerOptions.emitDecoratorMetadata) { + writeLines(metadataHelper); + } + + decorateEmitted = true; + helpersEmitted = true; + } + + if (!paramEmitted && node.flags & NodeFlags.HasParamDecorators) { + writeLines(paramHelper); + paramEmitted = true; + helpersEmitted = true; + } + + if (!awaiterEmitted && node.flags & NodeFlags.HasAsyncFunctions) { + writeLines(awaiterHelper); + awaiterEmitted = true; + helpersEmitted = true; + } + + if (helpersEmitted) { + writeLine(); + } + } + + return helpersEmitted; + } + + function emitExportStarHelper() { + writeLines(exportStarHelper); + } + + function writeLines(text: string): void { + const lines = text.split(/\r\n|\r|\n/g); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line.length) { + if (i > 0) { + writeLine(); + } + write(line); + } + } + } + + // + // Helpers + // + + function emitShebang() { + const shebang = getShebang(currentText); + if (shebang) { + write(shebang); + } + } + + function emitModifiers(node: Node, modifiers: ModifiersArray) { + const startingPos = writer.getTextPos(); + emitList(node, modifiers, ListFormat.SingleLine); + + const endingPos = writer.getTextPos(); + if (startingPos !== endingPos) { + write(" "); + } + } + + function emitWithPrefix(prefix: string, node: Node) { + emitNodeWithPrefix(prefix, node, emit); + } + + function emitExpressionWithPrefix(prefix: string, node: Node) { + emitNodeWithPrefix(prefix, node, emitExpression); + } + + function emitNodeWithPrefix(prefix: string, node: Node, emit: (node: Node) => void) { + if (node) { + write(prefix); + emit(node); + } + } + + function emitWithSuffix(node: Node, suffix: string) { + if (node) { + emit(node); + write(suffix); + } + } + + function tryEmitSubstitute(node: Node, substitution: (node: Node) => Node) { + const substitute = substitution ? substitution(node) : node; + if (substitute && substitute !== node) { + emitWorker(substitute); + return true; + } + + return false; + } + + function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean { + const constantValue = tryGetConstEnumValue(node); + if (constantValue !== undefined) { + write(String(constantValue)); + if (!compilerOptions.removeComments) { + const propertyName = isPropertyAccessExpression(node) + ? declarationNameToString(node.name) + : getTextOfNode(node.argumentExpression); + write(` /* ${propertyName} */`); + } + + return true; + } + + return false; + } + + function emitEmbeddedStatement(node: Statement) { + if (isBlock(node)) { + write(" "); + emit(node); + } + else { + writeLine(); + increaseIndent(); + emit(node); + decreaseIndent(); + } + } + + function emitDecorators(parentNode: Node, decorators: NodeArray) { + emitList(parentNode, decorators, ListFormat.Decorators); + } + + function emitTypeArguments(parentNode: Node, typeArguments: NodeArray) { + emitList(parentNode, typeArguments, ListFormat.TypeArguments); + } + + function emitTypeParameters(parentNode: Node, typeParameters: NodeArray) { + emitList(parentNode, typeParameters, ListFormat.TypeParameters); + } + + function emitParameters(parentNode: Node, parameters: NodeArray) { + emitList(parentNode, parameters, ListFormat.Parameters); + } + + function emitParametersForArrow(parentNode: Node, parameters: NodeArray) { + if (parameters && + parameters.length === 1 && + parameters[0].type === undefined && + parameters[0].pos === parentNode.pos) { + emit(parameters[0]); + } + else { + emitParameters(parentNode, parameters); + } + } + + function emitParametersForIndexSignature(parentNode: Node, parameters: NodeArray) { + emitList(parentNode, parameters, ListFormat.IndexSignatureParameters); + } + + function emitList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) { + emitNodeList(emit, parentNode, children, format, start, count); + } + + function emitExpressionList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) { + emitNodeList(emitExpression, parentNode, children, format, start, count); + } + + function emitNodeList(emit: (node: Node) => void, parentNode: Node, children: NodeArray, format: ListFormat, start = 0, count = children ? children.length - start : 0) { + const isUndefined = children === undefined; + if (isUndefined && format & ListFormat.OptionalIfUndefined) { + return; + } + + const isEmpty = isUndefined || children.length === 0 || start >= children.length || count === 0; + if (isEmpty && format & ListFormat.OptionalIfEmpty) { + return; + } + + if (format & ListFormat.BracketsMask) { + write(getOpeningBracket(format)); + } + + if (isEmpty) { + // Write a line terminator if the parent node was multi-line + if (format & ListFormat.MultiLine) { + writeLine(); + } + else if (format & ListFormat.SpaceBetweenBraces) { + write(" "); + } + } + else { + // Write the opening line terminator or leading whitespace. + if (shouldWriteLeadingLineTerminator(parentNode, children, format)) { + writeLine(); + } + else if (format & ListFormat.SpaceBetweenBraces) { + write(" "); + } + + // Increase the indent, if requested. + if (format & ListFormat.Indented) { + increaseIndent(); + } + + // Emit each child. + let previousSibling: Node; + const delimiter = getDelimiter(format); + for (let i = 0; i < count; i++) { + const child = children[start + i]; + + // Write the delimiter if this is not the first node. + if (previousSibling) { + write(delimiter); + + // Write either a line terminator or whitespace to separate the elements. + if (shouldWriteSeparatingLineTerminator(previousSibling, child, format)) { + writeLine(); + } + else if (previousSibling) { + write(" "); + } + } + + // Emit this child. + emit(child); + + previousSibling = child; + } + + // Write a trailing comma, if requested. + const hasTrailingComma = (format & ListFormat.AllowTrailingComma) && children.hasTrailingComma; + if (format & ListFormat.CommaDelimited && hasTrailingComma) { + write(","); + } + + // Decrease the indent, if requested. + if (format & ListFormat.Indented) { + decreaseIndent(); + } + + // Write the closing line terminator or closing whitespace. + if (shouldWriteClosingLineTerminator(parentNode, children, format)) { + writeLine(); + } + else if (format & ListFormat.SpaceBetweenBraces) { + write(" "); + } + } + + if (format & ListFormat.BracketsMask) { + write(getClosingBracket(format)); + } + } + + function writeIfAny(nodes: NodeArray, text: string) { + if (nodes && nodes.length > 0) { + write(text); + } + } + + function writeIfPresent(node: Node, text: string) { + if (node !== undefined) { + write(text); + } + } + + function writeToken(token: SyntaxKind, pos?: number) { + const tokenStartPos = skipTrivia(currentText, pos); + emitPos(tokenStartPos); + const tokenEndPos = writeTokenText(token, pos); + emitPos(tokenEndPos); + return tokenEndPos; + } + + function writeTokenText(token: SyntaxKind, pos?: number) { + const tokenString = tokenToString(token); + write(tokenString); + return positionIsSynthesized(pos) ? -1 : pos + tokenString.length; + } + + function writeTokenNode(node: Node) { + if (node) { + emitStart(node); + writeTokenText(node.kind); + emitEnd(node); + } + } + + function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) { + if (value) { + increaseIndent(); + writeLine(); + } + else if (valueToWriteWhenNotIndenting) { + write(valueToWriteWhenNotIndenting); + } + } + + // Helper function to decrease the indent if we previously indented. Allows multiple + // previous indent values to be considered at a time. This also allows caller to just + // call this once, passing in all their appropriate indent values, instead of needing + // to call this helper function multiple times. + function decreaseIndentIf(value1: boolean, value2?: boolean) { + if (value1) { + decreaseIndent(); + } + if (value2) { + decreaseIndent(); + } + } + + function shouldWriteLeadingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { + if (format & ListFormat.MultiLine) { + return true; + } + else if (format & ListFormat.PreserveLines) { + if (getNodeEmitFlags(parentNode) & NodeEmitFlags.MultiLine) { + return true; + } + + const firstChild = children[0]; + if (firstChild === undefined) { + return !positionsAreOnSameLine(getStartPos(parentNode), parentNode.end); + } + else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { + return synthesizedNodeStartsOnNewLine(firstChild, format); + } + else { + return !rangeStartPositionsAreOnSameLine(parentNode, firstChild); + } + } + else { + return false; + } + } + + function shouldWriteSeparatingLineTerminator(previousNode: Node, nextNode: Node, format: ListFormat) { + if (format & ListFormat.MultiLine) { + return true; + } + else if (format & ListFormat.PreserveLines) { + if (previousNode === undefined || nextNode === undefined) { + return false; + } + else if (nodeIsSynthesized(previousNode) || nodeIsSynthesized(nextNode)) { + return synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format); + } + else { + return !rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode); + } + } + else { + return false; + } + } + + function shouldWriteClosingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { + if (format & ListFormat.MultiLine) { + return true; + } + else if (format & ListFormat.PreserveLines) { + if (getNodeEmitFlags(parentNode) & NodeEmitFlags.MultiLine) { + return true; + } + + const lastChild = lastOrUndefined(children); + if (lastChild === undefined) { + return !positionsAreOnSameLine(getStartPos(parentNode), parentNode.end); + } + else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { + return synthesizedNodeStartsOnNewLine(lastChild, format); + } + else { + return !rangeEndPositionsAreOnSameLine(parentNode, lastChild); + } + } + else { + return false; + } + } + + function synthesizedNodeStartsOnNewLine(node: Node, format?: ListFormat) { + if (nodeIsSynthesized(node)) { + const startsOnNewLine = (node).startsOnNewLine; + if (startsOnNewLine === undefined) { + return (format & ListFormat.PreferNewLine) !== 0; + } + + return startsOnNewLine; + } + return (format & ListFormat.PreferNewLine) !== 0; + } + + function rangeStartPositionsAreOnSameLine(range1: TextRange, range2: TextRange) { + return positionsAreOnSameLine(getStartPos(range1), getStartPos(range2)); + } + + function rangeEndPositionsAreOnSameLine(range1: TextRange, range2: TextRange) { + return positionsAreOnSameLine(range1.end, range2.end); + } + + function rangeEndIsOnSameLineAsRangeStart(range1: TextRange, range2: TextRange) { + return positionsAreOnSameLine(range1.end, getStartPos(range2)); + } + + function positionsAreOnSameLine(pos1: number, pos2: number) { + return pos1 === pos2 || + getLineOfLocalPosition(currentSourceFile, pos1) === getLineOfLocalPosition(currentSourceFile, pos2); + } + + function getStartPos(range: TextRange) { + return range.pos === -1 ? -1 : skipTrivia(currentText, range.pos); + } + + function needsIndentation(parent: Node, node1: Node, node2: Node): boolean { + // Always use a newline for synthesized code if the synthesizer desires it. + if (synthesizedNodeStartsOnNewLine(node2)) { + return true; + } + + return !nodeIsSynthesized(parent) + && !nodeIsSynthesized(node1) + && !nodeIsSynthesized(node2) + && !rangeEndIsOnSameLineAsRangeStart(node1, node2); + } + + function getTextOfNode(node: Node, includeTrivia?: boolean) { + if (nodeIsSynthesized(node) && (isLiteralExpression(node) || isIdentifier(node))) { + return node.text; + } + + return getSourceTextOfNodeFromSourceFile(currentSourceFile, node, includeTrivia); + } + + function tryGetConstEnumValue(node: Node): number { + if (compilerOptions.isolatedModules) { + return undefined; + } + + return isPropertyAccessExpression(node) || isElementAccessExpression(node) + ? resolver.getConstantValue(node) + : undefined; + } + + function isSingleLineEmptyBlock(block: Block) { + return (getNodeEmitFlags(block) & NodeEmitFlags.MultiLine) === 0 && + block.statements.length === 0 && + rangeEndIsOnSameLineAsRangeStart(block, block); + } + + function tempKindToFlags(kind: TempVariableKind) { + return kind === TempVariableKind.Loop + ? TempFlags._i + : TempFlags.Auto; + } + + /** + * 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. + */ + function makeTempVariableName(flags: TempFlags): string { + if (flags && !(tempFlags & flags)) { + const name = flags === TempFlags._i ? "_i" : "_n"; + if (isUniqueName(name)) { + tempFlags |= flags; + return name; + } + } + while (true) { + const count = tempFlags & TempFlags.CountMask; + tempFlags++; + // Skip over 'i' and 'n' + if (count !== 8 && count !== 13) { + const name = count < 26 + ? "_" + String.fromCharCode(CharacterCodes.a + count) + : "_" + (count - 26); + if (isUniqueName(name)) { + return name; + } + } + } + } + } + } + + 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 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 getClosingBracket(format: ListFormat) { + return brackets[format & ListFormat.BracketsMask][1]; + } + + const enum ListFormat { + None = 0, + + // Line separators + SingleLine = 1 << 0, // Prints the list on a single line (default). + MultiLine = 1 << 1, // Prints the list on multiple lines. + PreserveLines = 1 << 2, // Prints the list using line preservation if possible. + + // Delimiters + NotDelimited = 0, // There is no delimiter between list items (default). + BarDelimited = 1 << 3, // Each list item is space-and-bar (" |") delimited. + AmpersandDelimited = 1 << 4, // Each list item is space-and-ampersand (" &") delimited. + CommaDelimited = 1 << 5, // Each list item is comma (",") delimited. + AllowTrailingComma = 1 << 6, // Write a trailing comma (",") if present. + DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited, + + // Whitespace + Indented = 1 << 7, // The list should be indented. + SpaceBetweenBraces = 1 << 8, // Inserts a space after the opening brace and before the closing brace. + + // Brackets/Braces + Braces = 1 << 9, // The list is surrounded by "{" and "}". + Parenthesis = 1 << 10, // The list is surrounded by "(" and ")". + AngleBrackets = 1 << 11, // The list is surrounded by "<" and ">". + SquareBrackets = 1 << 12, // The list is surrounded by "[" and "]". + BracketsMask = Braces | Parenthesis | AngleBrackets | SquareBrackets, + OptionalIfUndefined = 1 << 13, // Do not emit brackets if the list is undefined. + OptionalIfEmpty = 1 << 14, // Do not emit brackets if the list is empty. + Optional = OptionalIfUndefined | OptionalIfEmpty, + + // Other + PreferNewLine = 1 << 15, // Prefer adding a LineTerminator between synthesized nodes. + + // Precomputed Formats + TypeLiteralMembers = MultiLine | Indented, + TupleTypeElements = CommaDelimited | SingleLine | Indented, + UnionTypeConstituents = BarDelimited | SingleLine, + IntersectionTypeConstituents = AmpersandDelimited | SingleLine, + ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces, + ArrayBindingPatternElements = SingleLine | AllowTrailingComma, + ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenBraces | Indented | Braces, + ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | AllowTrailingComma | Indented | SquareBrackets, + CallExpressionArguments = CommaDelimited | SingleLine | Parenthesis, + NewExpressionArguments = CommaDelimited | SingleLine | Parenthesis | OptionalIfUndefined, + TemplateExpressionSpans = SingleLine, + SingleLineBlockStatements = SpaceBetweenBraces | SingleLine, + MultiLineBlockStatements = Indented | MultiLine, + VariableDeclarationList = CommaDelimited | SingleLine, + SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenBraces, + MultiLineFunctionBodyStatements = MultiLine | Indented, + ClassHeritageClauses = SingleLine, + ClassMembers = Indented | MultiLine, + InterfaceMembers = Indented | MultiLine, + EnumMembers = CommaDelimited | Indented | MultiLine, + CaseBlockClauses = Indented | MultiLine, + NamedImportsOrExportsElements = CommaDelimited | AllowTrailingComma | SingleLine | SpaceBetweenBraces, + JsxElementChildren = SingleLine, + JsxElementAttributes = SingleLine, + CaseOrDefaultClauseStatements = Indented | MultiLine, + HeritageClauseTypes = CommaDelimited | SingleLine, + SourceFileStatements = MultiLine, + Decorators = MultiLine | Optional, + TypeArguments = CommaDelimited | SingleLine | Indented | AngleBrackets | Optional, + TypeParameters = CommaDelimited | SingleLine | Indented | AngleBrackets | Optional, + Parameters = CommaDelimited | SingleLine | Indented | Parenthesis, + IndexSignatureParameters = CommaDelimited | SingleLine | Indented | SquareBrackets, + } +} \ No newline at end of file diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index d6d3dedd36131..0507442642e54 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -2,6 +2,12 @@ /* @internal */ namespace ts { + export function getTransformers(compilerOptions: CompilerOptions) { + const transformers: Transformer[] = []; + // TODO(rbuckton): Add transformers + return transformers; + } + /** * Transforms an array of SourceFiles by passing them through each transformer. * diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index 29f39b983f399..07b59ab67bd28 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -20,6 +20,9 @@ "factory.ts", "visitor.ts", "transformer.ts", + "comments.ts", + "printer.ts", + "declarationEmitter.ts", "emitter.ts", "program.ts", "commandLineParser.ts", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 772c90fb08cca..70338d3d0f2bb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2795,6 +2795,7 @@ namespace ts { UMDDefine = 1 << 2, // This node should be replaced with the UMD define helper. NoLexicalEnvironment = 1 << 3, // A new LexicalEnvironment should *not* be introduced when emitting this node. SingleLine = 1 << 4, // The contents of this node should be emit on a single line. + MultiLine = 1 << 5, // The contents of this node should be emit on multiple lines. } /** Additional context provided to `visitEachChild` */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0f1d27a28f237..2ab7f15549b92 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -102,6 +102,22 @@ namespace ts { return true; } + export function getLanguageVersion(compilerOptions: CompilerOptions) { + return compilerOptions.target || ScriptTarget.ES3; + } + + export function getModuleKind(compilerOptions: CompilerOptions) { + if (compilerOptions.module) { + return compilerOptions.module; + } + + if (getLanguageVersion(compilerOptions) === ScriptTarget.ES6) { + return ModuleKind.ES6; + } + + return ModuleKind.None; + } + export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean { return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText); } @@ -242,6 +258,60 @@ namespace ts { return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node, includeTrivia); } + export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile, languageVersion: ScriptTarget) { + // Any template literal or string literal with an extended escape + // (e.g. "\u{0067}") will need to be downleveled as a escaped string literal. + if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) { + return getQuotedEscapedLiteralText("\"", node.text, "\""); + } + + // If we don't need to downlevel and we can reach the original source text using + // the node's parent reference, then simply get the text as it was originally written. + if (!nodeIsSynthesized(node) && node.parent) { + const text = getSourceTextOfNodeFromSourceFile(sourceFile, node); + if (languageVersion < ScriptTarget.ES6 && isBinaryOrOctalIntegerLiteral(node, text)) { + return node.text; + } + return text; + } + + // If we can't reach the original source text, use the canonical form if it's a number, + // or an escaped quoted form of the original text if it's string-like. + switch (node.kind) { + case SyntaxKind.StringLiteral: + return getQuotedEscapedLiteralText("\"", node.text, "\""); + case SyntaxKind.NoSubstitutionTemplateLiteral: + return getQuotedEscapedLiteralText("`", node.text, "`"); + case SyntaxKind.TemplateHead: + return getQuotedEscapedLiteralText("`", node.text, "${"); + case SyntaxKind.TemplateMiddle: + return getQuotedEscapedLiteralText("}", node.text, "${"); + case SyntaxKind.TemplateTail: + return getQuotedEscapedLiteralText("}", node.text, "`"); + case SyntaxKind.NumericLiteral: + return node.text; + } + + Debug.fail(`Literal kind '${node.kind}' not accounted for.`); + } + + export function isBinaryOrOctalIntegerLiteral(node: LiteralLikeNode, text: string) { + if (node.kind === SyntaxKind.NumericLiteral && text.length > 1) { + switch (text.charCodeAt(1)) { + case CharacterCodes.b: + case CharacterCodes.B: + case CharacterCodes.o: + case CharacterCodes.O: + return true; + } + } + return false; + } + + function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) { + return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote; + } + // Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__' export function escapeIdentifier(identifier: string): string { return identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier; @@ -2765,7 +2835,7 @@ namespace ts { return isUnaryExpressionKind(node.kind); } - function isExpressionKind(kind: SyntaxKind): boolean { + export function isExpressionKind(kind: SyntaxKind): boolean { return kind === SyntaxKind.ConditionalExpression || kind === SyntaxKind.YieldExpression || kind === SyntaxKind.ArrowFunction From 49d2d933796c2a8bdfa55d5e2830a97b3766c9a8 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 9 Feb 2016 15:27:33 -0800 Subject: [PATCH 02/43] Adds destructuring logic and placeholders for each transformer. --- src/compiler/factory.ts | 33 +- src/compiler/sourcemap.ts | 37 ++- src/compiler/transformer.ts | 36 ++- src/compiler/transformers/destructuring.ts | 347 +++++++++++++++++++++ src/compiler/transformers/es6.ts | 30 ++ src/compiler/transformers/es7.ts | 32 ++ src/compiler/transformers/jsx.ts | 30 ++ src/compiler/transformers/module/es6.ts | 17 + src/compiler/transformers/module/module.ts | 18 ++ src/compiler/transformers/module/system.ts | 18 ++ src/compiler/transformers/ts.ts | 30 ++ src/compiler/types.ts | 4 +- src/compiler/utilities.ts | 65 +++- 13 files changed, 682 insertions(+), 15 deletions(-) create mode 100644 src/compiler/transformers/destructuring.ts create mode 100644 src/compiler/transformers/es6.ts create mode 100644 src/compiler/transformers/es7.ts create mode 100644 src/compiler/transformers/jsx.ts create mode 100644 src/compiler/transformers/module/es6.ts create mode 100644 src/compiler/transformers/module/module.ts create mode 100644 src/compiler/transformers/module/system.ts create mode 100644 src/compiler/transformers/ts.ts diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 0d225184a4ba5..242c67f51b49e 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -135,9 +135,18 @@ namespace ts { return node; } - export function createTempVariable(tempKind: TempVariableKind): Identifier { + export function createTempVariable(): Identifier { const name = createNode(SyntaxKind.Identifier); - name.tempKind = tempKind; + name.text = undefined; + name.tempKind = TempVariableKind.Auto; + getNodeId(name); + return name; + } + + export function createLoopVariable(): Identifier { + const name = createNode(SyntaxKind.Identifier); + name.text = undefined; + name.tempKind = TempVariableKind.Loop; getNodeId(name); return name; } @@ -171,16 +180,16 @@ namespace ts { return createVoid(createLiteral(0)); } - export function createPropertyAccess(expression: Expression, name: string | Identifier) { - const node = createNode(SyntaxKind.PropertyAccessExpression); + export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange) { + const node = createNode(SyntaxKind.PropertyAccessExpression, location); node.expression = parenthesizeForAccess(expression); node.dotToken = createSynthesizedNode(SyntaxKind.DotToken); node.name = coerceIdentifier(name); return node; } - export function createElementAccess(expression: Expression, index: string | number | Expression) { - const node = createNode(SyntaxKind.ElementAccessExpression); + export function createElementAccess(expression: Expression, index: string | number | Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ElementAccessExpression, location); node.expression = parenthesizeForAccess(expression); node.argumentExpression = coerceExpression(index); return node; @@ -216,8 +225,8 @@ namespace ts { return createBinary(left, SyntaxKind.CommaToken, right); } - export function createCall(expression: Expression, argumentsArray: Expression[]) { - const node = createNode(SyntaxKind.CallExpression); + export function createCall(expression: Expression, argumentsArray: Expression[], location?: TextRange) { + const node = createNode(SyntaxKind.CallExpression, location); node.expression = parenthesizeForAccess(expression); node.arguments = createNodeArray(argumentsArray); return node; @@ -228,6 +237,14 @@ namespace ts { return createCall(createPropertyAccess(array, "slice"), argumentsList); } + export function createMathPow(left: Expression, right: Expression, location?: TextRange) { + return createCall( + createPropertyAccess(createIdentifier("Math"), "pow"), + [left, right], + location + ); + } + export function parenthesizeExpression(expression: Expression) { const node = createNode(SyntaxKind.ParenthesizedExpression); node.expression = expression; diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 86bad38cb6e1f..5e453b1b10112 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -7,12 +7,15 @@ namespace ts { setSourceFile(sourceFile: SourceFile): void; emitPos(pos: number): void; emitStart(range: TextRange): void; - emitEnd(range: TextRange, stopOverridingSpan?: boolean): void; - changeEmitSourcePos(): void; + emitEnd(range: TextRange): void; + /*@deprecated*/ emitEnd(range: TextRange, stopOverridingSpan: boolean): void; + /*@deprecated*/ changeEmitSourcePos(): void; getText(): string; getSourceMappingURL(): string; initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void; reset(): void; + enable(): void; + disable(): void; } let nullSourceMapWriter: SourceMapWriter; @@ -38,6 +41,8 @@ namespace ts { getSourceMappingURL(): string { return undefined; }, initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void { }, reset(): void { }, + enable(): void { }, + disable(): void { } }; } @@ -62,6 +67,8 @@ namespace ts { // Source map data let sourceMapData: SourceMapData; + let disableDepth: number; + return { getSourceMapData: () => sourceMapData, setSourceFile, @@ -73,6 +80,8 @@ namespace ts { getSourceMappingURL, initialize, reset, + enable, + disable, }; function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { @@ -81,6 +90,7 @@ namespace ts { } currentSourceFile = undefined; + disableDepth = 0; // Current source map file and its index in the sources list sourceMapSourceIndex = -1; @@ -147,6 +157,17 @@ namespace ts { lastEncodedSourceMapSpan = undefined; lastEncodedNameIndex = undefined; sourceMapData = undefined; + disableDepth = 0; + } + + function enable() { + if (disableDepth > 0) { + disableDepth--; + } + } + + function disable() { + disableDepth++; } function updateLastEncodedAndRecordedSpans() { @@ -168,7 +189,7 @@ namespace ts { sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] : defaultLastEncodedSourceMapSpan; - // TODO: Update lastEncodedNameIndex + // TODO: Update lastEncodedNameIndex // Since we dont support this any more, lets not worry about it right now. // When we start supporting nameIndex, we will get back to this @@ -236,7 +257,7 @@ namespace ts { } function emitPos(pos: number) { - if (pos === -1) { + if (positionIsSynthesized(pos) || disableDepth > 0) { return; } @@ -288,9 +309,17 @@ namespace ts { function emitStart(range: TextRange) { emitPos(getStartPos(range)); + + if ((range).disableSourceMap) { + disable(); + } } function emitEnd(range: TextRange, stopOverridingEnd?: boolean) { + if ((range).disableSourceMap) { + enable(); + } + emitPos(range.end); stopOverridingSpan = stopOverridingEnd; } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 0507442642e54..3e6e1dcf6e59d 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -1,10 +1,44 @@ /// +/// +/// +/// +/// +/// +/// +/// + /* @internal */ namespace ts { + const moduleTransformerMap: Map = { + [ModuleKind.ES6]: transformES6Module, + [ModuleKind.System]: transformSystemModule, + [ModuleKind.AMD]: transformModule, + [ModuleKind.CommonJS]: transformModule, + [ModuleKind.UMD]: transformModule, + [ModuleKind.None]: transformModule + }; + export function getTransformers(compilerOptions: CompilerOptions) { + const jsx = compilerOptions.jsx; + const languageVersion = getLanguageVersion(compilerOptions); + const moduleKind = getModuleKind(compilerOptions); const transformers: Transformer[] = []; - // TODO(rbuckton): Add transformers + + transformers.push(transformTypeScript); + transformers.push(moduleTransformerMap[moduleKind]); + if (jsx === JsxEmit.React) { + transformers.push(transformJsx); + } + + if (languageVersion < ScriptTarget.ES7) { + transformers.push(transformES7); + } + + if (languageVersion < ScriptTarget.ES6) { + transformers.push(transformES6); + } + return transformers; } diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts new file mode 100644 index 0000000000000..19c25e5699f15 --- /dev/null +++ b/src/compiler/transformers/destructuring.ts @@ -0,0 +1,347 @@ +/// +/// + +/*@internal*/ +namespace ts { + /** + * Flattens a destructuring assignment expression. + * + * @param root The destructuring assignment expression. + * @param needsValue Indicates whether the value from the right-hand-side of the + * destructuring assignment is needed as part of a larger expression. + * @param recordTempVariable A callback used to record new temporary variables. + */ + export function flattenDestructuringAssignment(node: BinaryExpression, needsValue: boolean, recordTempVariable: (node: Identifier) => void) { + let location: TextRange = node; + let value = node.right; + if (isEmptyObjectLiteralOrArrayLiteral(node.left)) { + return value; + } + + const expressions: Expression[] = []; + if (needsValue) { + // Temporary assignment needed to emit root should highlight whole binary expression + value = ensureIdentifier(node.right, /*reuseIdentifierExpressions*/ true, node, emitTempVariableAssignment); + } + else if (nodeIsSynthesized(node)) { + // Source map node for root.left = root.right is root + // but if root is synthetic, which could be in below case, use the target which is { a } + // for ({a} of {a: string}) { + // } + location = node.right; + } + + flattenDestructuring(node, value, location, emitAssignment, emitTempVariableAssignment); + + if (needsValue) { + expressions.push(value); + } + + const expression = inlineExpressions(expressions); + aggregateTransformFlags(expression); + return expression; + + function emitAssignment(name: Identifier, value: Expression, location: TextRange) { + const expression = createAssignment(name, value, location); + if (isSimpleExpression(value)) { + (expression).disableSourceMap = true; + } + + aggregateTransformFlags(expression); + expressions.push(expression); + } + + function emitTempVariableAssignment(value: Expression, location: TextRange) { + const name = createTempVariable(); + recordTempVariable(name); + emitAssignment(name, value, location); + return name; + } + } + + /** + * Flattens binding patterns in a parameter declaration. + * + * @param node The ParameterDeclaration to flatten. + * @param value The rhs value for the binding pattern. + */ + export function flattenParameterDestructuring(node: ParameterDeclaration, value: Expression) { + const declarations: VariableDeclaration[] = []; + + flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment); + + return declarations; + + function emitAssignment(name: Identifier, value: Expression, location: TextRange) { + const declaration = createVariableDeclaration(name, value, location); + if (isSimpleExpression(value)) { + (declaration).disableSourceMap = true; + } + + aggregateTransformFlags(declaration); + declarations.push(declaration); + } + + function emitTempVariableAssignment(value: Expression, location: TextRange) { + const name = createTempVariable(); + emitAssignment(name, value, location); + return name; + } + } + + /** + * Flattens binding patterns in a variable declaration. + * + * @param node The VariableDeclaration to flatten. + * @param value An optional rhs value for the binding pattern. + */ + export function flattenVariableDestructuring(node: VariableDeclaration, value?: Expression) { + const declarations: VariableDeclaration[] = []; + + flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment); + + return declarations; + + function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) { + const declaration = createVariableDeclaration(name, value, location); + if (declarations.length === 0) { + declaration.pos = -1; + } + + if (isSimpleExpression(value)) { + (declaration).disableSourceMap = true; + } + + declaration.original = original; + declarations.push(declaration); + aggregateTransformFlags(declaration); + } + + function emitTempVariableAssignment(value: Expression, location: TextRange) { + const name = createTempVariable(); + emitAssignment(name, value, location, /*original*/ undefined); + return name; + } + } + + /** + * Flattens binding patterns in a variable declaration and transforms them into an expression. + * + * @param node The VariableDeclaration to flatten. + * @param recordTempVariable A callback used to record new temporary variables. + */ + export function flattenVariableDestructuringToExpression(node: VariableDeclaration, recordTempVariable: (name: Identifier) => void) { + const pendingAssignments: Expression[] = []; + + flattenDestructuring(node, /*value*/ undefined, node, emitAssignment, emitTempVariableAssignment); + + const expression = inlineExpressions(pendingAssignments); + aggregateTransformFlags(expression); + return expression; + + function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) { + const expression = createAssignment(name, value, location); + if (isSimpleExpression(value)) { + (expression).disableSourceMap = true; + } + + expression.original = original; + pendingAssignments.push(expression); + } + + function emitTempVariableAssignment(value: Expression, location: TextRange) { + const name = createTempVariable(); + recordTempVariable(name); + emitAssignment(name, value, location, /*original*/ undefined); + return name; + } + } + + function flattenDestructuring( + root: BindingElement | BinaryExpression, + value: Expression, + location: TextRange, + emitAssignment: (name: Identifier, value: Expression, location: TextRange, original: Node) => void, + emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier) { + if (isBinaryExpression(root)) { + emitDestructuringAssignment(root.left, value, location) + } + else { + emitBindingElement(root, value); + } + + function emitDestructuringAssignment(bindingTarget: Expression | ShorthandPropertyAssignment, value: Expression, location: TextRange) { + // When emitting target = value use source map node to highlight, including any temporary assignments needed for this + let target: Expression; + if (isShortHandPropertyAssignment(bindingTarget)) { + if (bindingTarget.objectAssignmentInitializer) { + value = createDefaultValueCheck(value, bindingTarget.objectAssignmentInitializer, location); + } + + target = bindingTarget.name; + } + else if (isBinaryExpression(bindingTarget) && bindingTarget.operatorToken.kind === SyntaxKind.EqualsToken) { + value = createDefaultValueCheck(value, bindingTarget.right, location); + target = bindingTarget.left; + } + else { + target = bindingTarget; + } + + if (target.kind === SyntaxKind.ObjectLiteralExpression) { + emitObjectLiteralAssignment(target, value, location); + } + else if (target.kind === SyntaxKind.ArrayLiteralExpression) { + emitArrayLiteralAssignment(target, value, location); + } + else { + const name = cloneNode(target, /*location*/ target, /*flags*/ undefined, /*parent*/ undefined, /*original*/ target); + emitAssignment(name, value, location, /*original*/ undefined); + } + } + + function emitObjectLiteralAssignment(target: ObjectLiteralExpression, value: Expression, location: TextRange) { + const properties = target.properties; + if (properties.length !== 1) { + // For anything but a single element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. + // When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); + } + + for (const p of properties) { + if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) { + const propName = (p).name; + const target = p.kind === SyntaxKind.ShorthandPropertyAssignment ? p : (p).initializer || propName; + // Assignment for target = value.propName should highligh whole property, hence use p as source map node + emitDestructuringAssignment(target, createDestructuringPropertyAccess(value, propName), p); + } + } + } + + function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) { + const elements = target.elements; + if (elements.length !== 1) { + // For anything but a single element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. + // When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); + } + + for (let i = 0; i < elements.length; i++) { + const e = elements[i]; + if (e.kind !== SyntaxKind.OmittedExpression) { + // Assignment for target = value.propName should highligh whole property, hence use e as source map node + if (e.kind !== SyntaxKind.SpreadElementExpression) { + emitDestructuringAssignment(e, createElementAccess(value, createLiteral(i)), e); + } + else if (i === elements.length - 1) { + emitDestructuringAssignment((e).expression, createArraySlice(value, i), e); + } + } + } + } + + function emitBindingElement(target: BindingElement, value: Expression) { + // Any temporary assignments needed to emit target = value should point to target + if (target.initializer) { + // Combine value and initializer + value = value ? createDefaultValueCheck(value, target.initializer, target) : target.initializer; + } + else if (!value) { + // Use 'void 0' in absence of value and initializer + value = createVoidZero(); + } + + const name = target.name; + if (isBindingPattern(name)) { + const elements = name.elements; + const numElements = elements.length; + if (numElements !== 1) { + // For anything other than a single-element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. Additionally, if we have zero elements + // we need to emit *something* to ensure that in case a 'var' keyword was already emitted, + // so in that case, we'll intentionally create that temporary. + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0, target, emitTempVariableAssignment); + } + for (let i = 0; i < elements.length; i++) { + let element = elements[i]; + if (name.kind === SyntaxKind.ObjectBindingPattern) { + // Rewrite element to a declaration with an initializer that fetches property + let propName = element.propertyName || element.name; + emitBindingElement(element, createDestructuringPropertyAccess(value, propName)); + } + else if (element.kind !== SyntaxKind.OmittedExpression) { + if (!element.dotDotDotToken) { + // Rewrite element to a declaration that accesses array element at index i + emitBindingElement(element, createElementAccess(value, i)); + } + else if (i === elements.length - 1) { + emitBindingElement(element, createArraySlice(value, i)); + } + } + } + } + else { + const clonedName = cloneNode(name, /*location*/ undefined, /*flags*/ undefined, /*parent*/ undefined, /*original*/ name); + emitAssignment(clonedName, value, target, target); + } + } + + function createDefaultValueCheck(value: Expression, defaultValue: Expression, location: TextRange): Expression { + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); + return createConditional( + createStrictEquality(value, createVoidZero()), + defaultValue, + value + ); + } + + function createDestructuringPropertyAccess(object: Expression, propertyName: PropertyName): LeftHandSideExpression { + if (isComputedPropertyName(propertyName)) { + return createElementAccess( + object, + ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, propertyName, emitTempVariableAssignment) + ); + } + else if (isIdentifier(propertyName)) { + return createPropertyAccess( + object, + propertyName.text + ); + } + else { + // We create a synthetic copy of the identifier in order to avoid the rewriting that might + // otherwise occur when the identifier is emitted. + return createElementAccess( + object, + cloneNode(propertyName) + ); + } + } + } + + /** + * Ensures that there exists a declared identifier whose value holds the given expression. + * This function is useful to ensure that the expression's value can be read from in subsequent expressions. + * Unless 'reuseIdentifierExpressions' is false, 'value' will be returned if it is just an identifier. + * + * @param value the expression whose value needs to be bound. + * @param reuseIdentifierExpressions true if identifier expressions can simply be returned; + * false if it is necessary to always emit an identifier. + * @param location The location to use for source maps and comments. + * @param emitTempVariableAssignment A callback used to emit a temporary variable. + */ + function ensureIdentifier( + value: Expression, + reuseIdentifierExpressions: boolean, + location: TextRange, + emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier) { + if (isIdentifier(value) && reuseIdentifierExpressions) { + return value; + } + else { + return emitTempVariableAssignment(value, location); + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts new file mode 100644 index 0000000000000..d5c5f9566db79 --- /dev/null +++ b/src/compiler/transformers/es6.ts @@ -0,0 +1,30 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): ES6->ES5 transformer + export function transformES6(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + if (node.transformFlags & TransformFlags.ES6) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsES6) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts new file mode 100644 index 0000000000000..e4d1e564cb8b3 --- /dev/null +++ b/src/compiler/transformers/es7.ts @@ -0,0 +1,32 @@ +/// +/// + +/*@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 { + if (node.transformFlags & TransformFlags.ES7) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsES7) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node) { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts new file mode 100644 index 0000000000000..e9470b32eea8f --- /dev/null +++ b/src/compiler/transformers/jsx.ts @@ -0,0 +1,30 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): JSX->React transformer + export function transformJsx(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + if (node.transformFlags & TransformFlags.Jsx) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsJsx) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/module/es6.ts b/src/compiler/transformers/module/es6.ts new file mode 100644 index 0000000000000..b49d0a5c83b02 --- /dev/null +++ b/src/compiler/transformers/module/es6.ts @@ -0,0 +1,17 @@ +/// +/// + +/*@internal*/ +namespace ts { + export function transformES6Module(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts new file mode 100644 index 0000000000000..61c4a2cd8807c --- /dev/null +++ b/src/compiler/transformers/module/module.ts @@ -0,0 +1,18 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): CommonJS/AMD/UMD transformer + export function transformModule(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts new file mode 100644 index 0000000000000..e4c03d32b45d8 --- /dev/null +++ b/src/compiler/transformers/module/system.ts @@ -0,0 +1,18 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): System module transformer + export function transformSystemModule(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts new file mode 100644 index 0000000000000..0156bb5ff8434 --- /dev/null +++ b/src/compiler/transformers/ts.ts @@ -0,0 +1,30 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): TS->ES7 transformer + export function transformTypeScript(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node) { + if (node.transformFlags & TransformFlags.TypeScript) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsTypeScript) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 70338d3d0f2bb..6cd3a66678881 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2508,8 +2508,10 @@ namespace ts { ES3 = 0, ES5 = 1, ES6 = 2, + ES7 = 3, ES2015 = ES6, - Latest = ES6, + ES2016 = ES7, + Latest = ES7, } export const enum LanguageVariant { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2ab7f15549b92..15e288ef60133 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -11,7 +11,8 @@ namespace ts { export interface SynthesizedNode extends Node { leadingCommentRanges?: CommentRange[]; trailingCommentRanges?: CommentRange[]; - startsOnNewLine: boolean; + startsOnNewLine?: boolean; + disableSourceMap?: boolean; } export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration { @@ -2724,6 +2725,68 @@ namespace ts { return carriageReturnLineFeed; } + /** + * Tests whether a node and its subtree is simple enough to have its position + * information ignored when emitting source maps in a destructuring assignment. + * + * @param node The expression to test. + */ + export function isSimpleExpression(node: Expression): boolean { + return isSimpleExpressionWorker(node, 0); + } + + function isSimpleExpressionWorker(node: Expression, depth: number): boolean { + if (depth <= 5) { + const kind = node.kind; + if (kind === SyntaxKind.StringLiteral + || kind === SyntaxKind.NumericLiteral + || kind === SyntaxKind.RegularExpressionLiteral + || kind === SyntaxKind.NoSubstitutionTemplateLiteral + || kind === SyntaxKind.Identifier + || kind === SyntaxKind.ThisKeyword + || kind === SyntaxKind.SuperKeyword + || kind === SyntaxKind.TrueKeyword + || kind === SyntaxKind.FalseKeyword + || kind === SyntaxKind.NullKeyword) { + return true; + } + else if (kind === SyntaxKind.PropertyAccessExpression) { + return isSimpleExpressionWorker((node).expression, depth + 1); + } + else if (kind === SyntaxKind.ElementAccessExpression) { + return isSimpleExpressionWorker((node).expression, depth + 1) + && isSimpleExpressionWorker((node).argumentExpression, depth + 1); + } + else if (kind === SyntaxKind.PrefixUnaryExpression + || kind === SyntaxKind.PostfixUnaryExpression) { + return isSimpleExpressionWorker((node).operand, depth + 1); + } + else if (kind === SyntaxKind.BinaryExpression) { + return (node).operatorToken.kind !== SyntaxKind.AsteriskAsteriskToken + && isSimpleExpressionWorker((node).left, depth + 1) + && isSimpleExpressionWorker((node).right, depth + 1); + } + else if (kind === SyntaxKind.ConditionalExpression) { + return isSimpleExpressionWorker((node).condition, depth + 1) + && isSimpleExpressionWorker((node).whenTrue, depth + 1) + && isSimpleExpressionWorker((node).whenFalse, depth + 1) + } + else if (kind === SyntaxKind.VoidExpression + || kind === SyntaxKind.TypeOfExpression + || kind === SyntaxKind.DeleteExpression) { + return isSimpleExpressionWorker((node).expression, depth + 1); + } + else if (kind === SyntaxKind.ArrayLiteralExpression) { + return (node).elements.length === 0; + } + else if (kind === SyntaxKind.ObjectLiteralExpression) { + return (node).properties.length === 0; + } + } + + return false; + } + // Node tests // // All node tests in the following list should *not* reference parent pointers so that From f948b141857b0bcc66c799dbb9d04fca66816e63 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 12 Feb 2016 10:49:09 -0800 Subject: [PATCH 03/43] Adds the TypeScript transformer --- Jakefile.js | 16 + src/compiler/binder.ts | 6 + src/compiler/core.ts | 53 +- src/compiler/emitter.ts | 6 +- src/compiler/factory.ts | 753 +++++- src/compiler/printer.ts | 98 +- src/compiler/transformer.ts | 29 +- src/compiler/transformers/destructuring.ts | 75 +- src/compiler/transformers/ts.ts | 2719 +++++++++++++++++++- src/compiler/types.ts | 23 +- src/compiler/utilities.ts | 36 +- src/compiler/visitor.ts | 124 +- 12 files changed, 3725 insertions(+), 213 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 44cb1a61dbe65..69510545ea320 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -42,6 +42,14 @@ var compilerSources = [ "checker.ts", "factory.ts", "visitor.ts", + "transformers/destructuring.ts", + "transformers/ts.ts", + "transformers/module/es6.ts", + "transformers/module/system.ts", + "transformers/module/module.ts", + "transformers/jsx.ts", + "transformers/es7.ts", + "transformers/es6.ts", "transformer.ts", "sourcemap.ts", "comments.ts", @@ -67,6 +75,14 @@ var servicesSources = [ "checker.ts", "factory.ts", "visitor.ts", + "transformers/destructuring.ts", + "transformers/ts.ts", + "transformers/module/es6.ts", + "transformers/module/system.ts", + "transformers/module/module.ts", + "transformers/jsx.ts", + "transformers/es7.ts", + "transformers/es6.ts", "transformer.ts", "sourcemap.ts", "comments.ts", diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d1101ad57e150..b65c05ed3ca19 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1838,6 +1838,12 @@ namespace ts { transformFlags |= TransformFlags.AssertJsx; break; + case SyntaxKind.ExportKeyword: + // This node is both ES6 and TypeScript syntax. + transformFlags |= TransformFlags.AssertES6 | TransformFlags.TypeScript; + break; + + case SyntaxKind.DefaultKeyword: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.TemplateHead: case SyntaxKind.TemplateMiddle: diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 5f68777a63b5f..350a5165e6676 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -142,25 +142,44 @@ namespace ts { return count; } - export function filter(array: T[], f: (x: T) => boolean): T[] { + export function filter(array: T[], f: (x: T, i: number) => x is U): U[]; + export function filter(array: T[], f: (x: T, i: number) => boolean): T[]; + export function filter(array: T[], f: (x: T, i: number) => boolean): T[] { let result: T[]; if (array) { result = []; - for (const item of array) { - if (f(item)) { - result.push(item); + for (let i = 0; i < array.length; i++) { + const v = array[i]; + if (f(v, i)) { + result.push(v); } } } return result; } - export function map(array: T[], f: (x: T) => U): U[] { + export function map(array: T[], f: (x: T, i: number) => U): U[] { let result: U[]; if (array) { result = []; - for (const v of array) { - result.push(f(v)); + for (let i = 0; i < array.length; i++) { + const v = array[i]; + result.push(f(v, i)); + } + } + return result; + } + + export function flatMap(array: T[], f: (x: T, i: number) => 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) { + result = result.concat(ar); + } } } return result; @@ -212,15 +231,25 @@ namespace ts { return true; } + export function firstOrUndefined(array: T[]): T { + return array && array.length > 0 + ? array[0] + : undefined; + } + + export function singleOrUndefined(array: T[]): T { + return array && array.length === 1 + ? array[0] + : undefined; + } + /** * Returns the last element of an array if non-empty, undefined otherwise. */ export function lastOrUndefined(array: T[]): T { - if (array.length === 0) { - return undefined; - } - - return array[array.length - 1]; + return array && array.length > 0 + ? array[array.length - 1] + : undefined; } /** diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 4b1886fa8489a..ebac29967655d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -287,8 +287,12 @@ namespace ts { _i = 0x10000000, // Use/preference flag for '_i' } - // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { + return printFiles(resolver, host, targetSourceFile); + } + + // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature + export function legacyEmitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { // emit output for the __extends helper function const extendsHelper = ` var __extends = (this && this.__extends) || function (d, b) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 242c67f51b49e..35d017d3ca27f 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -6,33 +6,88 @@ namespace ts { let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; - function createNode(kind: SyntaxKind, location?: TextRange): Node { + function createNode(kind: SyntaxKind, location?: TextRange, flags?: NodeFlags): Node { const ConstructorForKind = kind === SyntaxKind.SourceFile ? (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor())) : (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor())); - return location + const node = location ? new ConstructorForKind(kind, location.pos, location.end) : new ConstructorForKind(kind, /*pos*/ -1, /*end*/ -1); + + if (flags) { + node.flags = flags; + } + + return node; } - export function createNodeArray(elements?: T[], pos?: number, end?: number): NodeArray { - const array = >(elements || []); - array.pos = pos; - array.end = end; + export function createNodeArray(elements?: T[], location?: TextRange): NodeArray { + if (elements) { + if (isNodeArray(elements)) { + return elements; + } + } + else { + elements = []; + } + + const array = >elements; + if (location) { + array.pos = location.pos; + array.end = location.end; + } + else { + array.pos = -1; + array.end = -1; + } + array.arrayKind = ArrayKind.NodeArray; return array; } - export function createModifiersArray(elements?: Modifier[], pos?: number, end?: number): ModifiersArray { - const array = (elements || []); - array.pos = pos; - array.end = end; + 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 = 0; + array.flags = flags; return array; } + function setModifiers(node: Node, modifiers: Modifier[]) { + if (modifiers) { + node.modifiers = createSynthesizedModifiersArray(modifiers); + node.flags |= node.modifiers.flags; + } + else { + node.modifiers = undefined; + } + } + export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node { const node = createNode(kind, /*location*/ undefined); node.startsOnNewLine = startsOnNewLine; @@ -40,11 +95,11 @@ namespace ts { } export function createSynthesizedNodeArray(elements?: T[]): NodeArray { - return createNodeArray(elements, /*pos*/ -1, /*end*/ -1); + return createNodeArray(elements, /*location*/ undefined); } export function createSynthesizedModifiersArray(elements?: Modifier[]): ModifiersArray { - return createModifiersArray(elements, /*pos*/ -1, /*end*/ -1); + return createModifiersArray(elements, /*location*/ undefined); } /** @@ -93,41 +148,28 @@ namespace ts { return node; } - export function createReturn(expression?: Expression): ReturnStatement { - const node = createSynthesizedNode(SyntaxKind.ReturnStatement); - node.expression = expression; - return node; - } - - export function createStatement(expression: Expression): ExpressionStatement { - const node = createSynthesizedNode(SyntaxKind.ExpressionStatement); - node.expression = expression; - return node; - } - - export function createVariableStatement(declarationList: VariableDeclarationList): VariableStatement { - const node = createSynthesizedNode(SyntaxKind.VariableStatement); - node.declarationList = declarationList; - return node; - } + // Literals - export function createVariableDeclarationList(declarations: VariableDeclaration[]): VariableDeclarationList { - const node = createSynthesizedNode(SyntaxKind.VariableDeclarationList); - node.declarations = createNodeArray(declarations); - return node; + export function createLiteral(value: string): StringLiteral; + export function createLiteral(value: number): LiteralExpression; + export function createLiteral(value: string | number | boolean): PrimaryExpression; + export function createLiteral(value: string | number | boolean): T { + if (typeof value === "number") { + const node = createNode(SyntaxKind.NumericLiteral); + node.text = value.toString(); + return node; + } + else if (typeof value === "boolean") { + return createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword); + } + else { + const node = createNode(SyntaxKind.StringLiteral); + node.text = String(value); + return node; + } } - export function createBlock(statements: Statement[]): Block { - const block = createSynthesizedNode(SyntaxKind.Block); - block.statements = createNodeArray(statements); - return block; - } - export function createVariableDeclaration(name: BindingPattern | Identifier, initializer?: Expression, location?: TextRange): VariableDeclaration { - const node = createNode(SyntaxKind.VariableDeclaration, location); - node.name = name; - node.initializer = initializer; - return node; - } + // Identifiers export function createIdentifier(text: string): Identifier { const node = createNode(SyntaxKind.Identifier); @@ -151,33 +193,102 @@ namespace ts { return name; } - export function createLiteral(value: string): StringLiteral; - export function createLiteral(value: number): LiteralExpression; - export function createLiteral(value: string | number | boolean): PrimaryExpression; - export function createLiteral(value: string | number | boolean): T { - if (typeof value === "number") { - const node = createNode(SyntaxKind.NumericLiteral); - node.text = value.toString(); - return node; - } - else if (typeof value === "boolean") { - return createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword); - } - else { - const node = createNode(SyntaxKind.StringLiteral); - node.text = String(value); - return node; - } + // Reserved words + + export function createSuper() { + const node = createNode(SyntaxKind.SuperKeyword); + return node; } - export function createVoid(expression: UnaryExpression) { - const node = createNode(SyntaxKind.VoidExpression); + export function createThis() { + const node = createNode(SyntaxKind.ThisKeyword); + return node; + } + + export function createNull() { + const node = createNode(SyntaxKind.NullKeyword); + return node; + } + + // Names + + export function createComputedPropertyName(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ComputedPropertyName, location); node.expression = expression; return node; } - export function createVoidZero() { - return createVoid(createLiteral(0)); + // Type members + + 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.name = coercePropertyName(name); + node.typeParameters = undefined; + node.parameters = createNodeArray(parameters); + node.body = body; + return node; + } + + export function createConstructor(parameters: ParameterDeclaration[], body: Block, location?: TextRange) { + const node = createNode(SyntaxKind.Constructor, location); + node.decorators = undefined; + node.modifiers = undefined; + node.typeParameters = undefined; + node.parameters = createSynthesizedNodeArray(parameters); + node.type = undefined; + node.body = body; + return node; + } + + 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.name = coercePropertyName(name); + node.typeParameters = undefined; + node.parameters = createNodeArray(); + node.body = body; + return node; + } + + 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.name = coercePropertyName(name); + node.typeParameters = undefined; + node.parameters = createNodeArray([parameter]); + node.body = body; + return node; + } + + export function createParameter(name: string | Identifier | BindingPattern, initializer?: Expression) { + const node = createNode(SyntaxKind.Parameter); + node.decorators = undefined; + node.modifiers = undefined; + node.dotDotDotToken = undefined; + node.name = coerceBindingName(name); + node.questionToken = undefined; + node.type = undefined; + node.initializer = initializer; + return node; + } + + + // Expression + + export function createArrayLiteral(elements?: Expression[]) { + const node = createNode(SyntaxKind.ArrayLiteralExpression); + node.elements = createNodeArray(elements); + return node; + } + + export function createObjectLiteral(properties?: ObjectLiteralElement[]) { + const node = createNode(SyntaxKind.ObjectLiteralExpression); + node.properties = createNodeArray(properties); + return node; } export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange) { @@ -195,6 +306,62 @@ namespace ts { return node; } + export function createCall(expression: Expression, argumentsArray: Expression[], location?: TextRange) { + const node = createNode(SyntaxKind.CallExpression, location); + node.expression = parenthesizeForAccess(expression); + node.arguments = createNodeArray(argumentsArray); + return node; + } + + export function createParen(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ParenthesizedExpression, location); + node.expression = expression; + return node; + } + + export function createFunctionExpression(asteriskToken: Node, name: string | Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange) { + const node = createNode(SyntaxKind.FunctionExpression, location); + node.modifiers = undefined; + node.asteriskToken = asteriskToken; + node.name = coerceIdentifier(name); + node.typeParameters = undefined; + node.parameters = createNodeArray(parameters); + node.type = undefined; + node.body = body; + return node; + } + + export function createArrowFunction(parameters: ParameterDeclaration[], body: Expression | Block, location?: TextRange) { + const node = createNode(SyntaxKind.ArrowFunction, location); + node.modifiers = undefined; + node.typeParameters = undefined; + node.parameters = createNodeArray(parameters); + node.type = undefined; + node.equalsGreaterThanToken = createNode(SyntaxKind.EqualsGreaterThanToken); + node.body = body; + return node; + } + + export function createTypeOf(expression: Expression) { + const node = createNode(SyntaxKind.TypeOfExpression); + node.expression = parenthesizeForUnary(expression); + return node; + } + + export function createVoid(expression: Expression) { + const node = createNode(SyntaxKind.VoidExpression); + node.expression = parenthesizeForUnary(expression); + return node; + } + + export function createBinary(left: Expression, operator: SyntaxKind, right: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.BinaryExpression, location); + node.left = parenthesizeForBinary(left, operator, BinaryOperand.Left); + node.operatorToken = createSynthesizedNode(operator); + node.right = parenthesizeForBinary(right, operator, BinaryOperand.Right); + return node; + } + export function createConditional(condition: Expression, whenTrue: Expression, whenFalse: Expression) { const node = createNode(SyntaxKind.ConditionalExpression); node.condition = condition; @@ -205,30 +372,184 @@ namespace ts { return node; } - export function createBinary(left: Expression, operator: SyntaxKind, right: Expression, location?: TextRange) { - const node = createNode(SyntaxKind.BinaryExpression, location); - node.left = parenthesizeForBinary(left, operator, BinaryOperand.Left); - node.operatorToken = createSynthesizedNode(operator); - node.right = parenthesizeForBinary(right, operator, BinaryOperand.Right); + export function createYield(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.YieldExpression, location); + node.expression = expression; + return node; + } + + export function createSpread(expression: Expression) { + const node = createNode(SyntaxKind.SpreadElementExpression); + node.expression = expression; return node; } + export function createClassExpression(name: Identifier, heritageClauses: HeritageClause[], members: ClassElement[], location?: TextRange) { + const node = createNode(SyntaxKind.ClassExpression, location); + node.decorators = undefined; + node.modifiers = undefined; + node.name = name; + node.typeParameters = undefined; + node.heritageClauses = createSynthesizedNodeArray(heritageClauses); + node.members = createSynthesizedNodeArray(members); + return node; + } + + export function createExpressionWithTypeArguments(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ExpressionWithTypeArguments, location); + node.typeArguments = undefined; + node.expression = parenthesizeForAccess(expression); + return node; + } + + // Element + + export function createBlock(statements: Statement[], location?: TextRange): Block { + const block = createNode(SyntaxKind.Block, location); + block.statements = createNodeArray(statements); + 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.declarationList = declarationList; + return node; + } + + export function createVariableDeclarationList(declarations: VariableDeclaration[], location?: TextRange, flags?: NodeFlags): VariableDeclarationList { + const node = createNode(SyntaxKind.VariableDeclarationList, location, flags); + node.declarations = createNodeArray(declarations); + return node; + } + + export function createLetDeclarationList(declarations: VariableDeclaration[], location?: TextRange) { + return createVariableDeclarationList(declarations, location, NodeFlags.Let); + } + + export function createConstDeclarationList(declarations: VariableDeclaration[], location?: TextRange) { + return createVariableDeclarationList(declarations, location, NodeFlags.Const); + } + + export function createVariableDeclaration(name: string | BindingPattern | Identifier, initializer?: Expression, location?: TextRange): VariableDeclaration { + const node = createNode(SyntaxKind.VariableDeclaration, location); + node.name = coerceBindingName(name); + node.initializer = initializer; + return node; + } + + export function createStatement(expression: Expression, location?: TextRange): ExpressionStatement { + const node = createNode(SyntaxKind.ExpressionStatement, location); + node.expression = expression; + return node; + } + + export function createReturn(expression?: Expression): ReturnStatement { + const node = createSynthesizedNode(SyntaxKind.ReturnStatement); + node.expression = expression; + return node; + } + + export function createFunctionDeclaration(modifiers: Modifier[], asteriskToken: Node, name: string | Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange) { + const node = createNode(SyntaxKind.FunctionDeclaration, location); + node.decorators = undefined; + setModifiers(node, modifiers); + node.asteriskToken = asteriskToken; + node.name = coerceIdentifier(name); + node.typeParameters = undefined; + node.parameters = createNodeArray(parameters); + node.type = undefined; + node.body = body; + 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.name = name; + node.typeParameters = undefined; + node.heritageClauses = createSynthesizedNodeArray(heritageClauses); + node.members = createSynthesizedNodeArray(members); + return node; + } + + export function createExportDefault(expression: Expression) { + const node = createNode(SyntaxKind.ExportAssignment); + node.isExportEquals = false; + node.expression = expression; + return node; + } + + export function createExportDeclaration(exportClause: NamedExports, moduleSpecifier?: Expression) { + const node = createNode(SyntaxKind.ExportDeclaration); + node.exportClause = exportClause; + node.moduleSpecifier = moduleSpecifier; + return node; + } + + export function createNamedExports(elements: ExportSpecifier[]) { + const node = createNode(SyntaxKind.NamedExports); + node.elements = createNodeArray(elements); + return node; + } + + export function createExportSpecifier(name: string | Identifier, propertyName?: string | Identifier) { + const node = createNode(SyntaxKind.ExportSpecifier); + node.name = coerceIdentifier(name); + node.propertyName = coerceIdentifier(propertyName); + return node; + } + + // Clauses + + export function createHeritageClause(token: SyntaxKind, types: ExpressionWithTypeArguments[], location?: TextRange) { + const node = createNode(SyntaxKind.HeritageClause, location); + node.token = token; + node.types = createSynthesizedNodeArray(types); + return node; + } + + // Compound nodes + export function createAssignment(left: Expression, right: Expression, location?: TextRange) { return createBinary(left, SyntaxKind.EqualsToken, right, location); } + export function createLogicalAnd(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.AmpersandAmpersandToken, right); + } + + export function createLogicalOr(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.BarBarToken, right); + } + export function createStrictEquality(left: Expression, right: Expression) { return createBinary(left, SyntaxKind.EqualsEqualsEqualsToken, right); } + export function createStrictInequality(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.ExclamationEqualsEqualsToken, right); + } + export function createComma(left: Expression, right: Expression) { return createBinary(left, SyntaxKind.CommaToken, right); } - export function createCall(expression: Expression, argumentsArray: Expression[], location?: TextRange) { - const node = createNode(SyntaxKind.CallExpression, location); - node.expression = parenthesizeForAccess(expression); - node.arguments = createNodeArray(argumentsArray); + export function createVoidZero() { + return createVoid(createLiteral(0)); + } + + 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); + } + + export function createRestParameter(name: string | Identifier) { + const node = createParameter(name, /*initializer*/ undefined); + node.dotDotDotToken = createSynthesizedNode(SyntaxKind.DotDotDotToken); return node; } @@ -245,16 +566,232 @@ namespace ts { ); } - export function parenthesizeExpression(expression: Expression) { - const node = createNode(SyntaxKind.ParenthesizedExpression); - node.expression = expression; - return node; + // Helpers + + export function createParamHelper(expression: Expression, parameterOffset: number) { + return createCall( + createIdentifier("__param"), + [ + createLiteral(parameterOffset), + expression + ] + ); + } + + export function createMetadataHelper(metadataKey: string, metadataValue: Expression, defer?: boolean) { + return createCall( + createIdentifier("__metadata"), + [ + createLiteral(metadataKey), + defer + ? createArrowFunction([], metadataValue) + : metadataValue + ] + ); + } + + export function createDecorateHelper(decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression) { + const argumentsArray: Expression[] = []; + argumentsArray.push(createArrayLiteral(decoratorExpressions)); + argumentsArray.push(target); + if (memberName) { + argumentsArray.push(memberName); + if (descriptor) { + argumentsArray.push(descriptor); + } + } + + return createCall(createIdentifier("__decorate"), argumentsArray); + } + + export function createAwaiterHelper(hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { + return createCall( + createIdentifier("__awaiter"), + [ + createThis(), + hasLexicalArguments ? createIdentifier("arguments") : createVoidZero(), + promiseConstructor ? createExpressionFromEntityName(promiseConstructor) : createVoidZero(), + createFunctionExpression( + createNode(SyntaxKind.AsteriskToken), + /*name*/ undefined, + [], + body + ) + ] + ); + } + + function createObjectCreate(prototype: Expression) { + return createCall( + createPropertyAccess(createIdentifier("Object"), "create"), + [prototype] + ); + } + + function createGeti(target: LeftHandSideExpression) { + // name => super[name] + return createArrowFunction( + [createParameter("name")], + createElementAccess( + target, + createIdentifier("name") + ) + ) + } + + function createSeti(target: LeftHandSideExpression) { + // (name, value) => super[name] = value + return createArrowFunction( + [ + createParameter("name"), + createParameter("value") + ], + createAssignment( + createElementAccess( + target, + createIdentifier("name") + ), + createIdentifier("value") + ) + ); + } + + export function createAdvancedAsyncSuperHelper() { + // const _super = (function (geti, seti) { + // const cache = Object.create(null); + // return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); + // })(name => super[name], (name, value) => super[name] = value); + + // const cache = Object.create(null); + const createCache = createVariableStatement( + /*modifiers*/ undefined, + createConstDeclarationList([ + createVariableDeclaration( + "cache", + createObjectCreate(createNull()) + ) + ]) + ); + + // get value() { return geti(name); } + const getter = createGetAccessor( + /*modifiers*/ undefined, + "value", + createBlock([ + createReturn( + createCall( + createIdentifier("geti"), + [createIdentifier("name")] + ) + ) + ]) + ); + + // set value(v) { seti(name, v); } + const setter = createSetAccessor( + /*modifiers*/ undefined, + "value", + createParameter("v"), + createBlock([ + createStatement( + createCall( + createIdentifier("seti"), + [ + createIdentifier("name"), + createIdentifier("v") + ] + ) + ) + ]) + ); + + // return name => cache[name] || ... + const getOrCreateAccessorsForName = createReturn( + createArrowFunction( + [createParameter("name")], + createLogicalOr( + createElementAccess( + createIdentifier("cache"), + createIdentifier("name") + ), + createParen( + createAssignment( + createElementAccess( + createIdentifier("cache"), + createIdentifier("name") + ), + createObjectLiteral([ + getter, + setter + ]) + ) + ) + ) + ) + ); + + // const _super = (function (geti, seti) { + // const cache = Object.create(null); + // return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); + // })(name => super[name], (name, value) => super[name] = value); + return createVariableStatement( + /*modifiers*/ undefined, + createConstDeclarationList([ + createVariableDeclaration( + "_super", + createCall( + createParen( + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + [ + createParameter("geti"), + createParameter("seti") + ], + createBlock([ + createCache, + getOrCreateAccessorsForName + ]) + ) + ), + [ + createGeti(createSuper()), + createSeti(createSuper()) + ] + ) + ) + ]) + ); + } + + export function createSimpleAsyncSuperHelper() { + return createVariableStatement( + /*modifiers*/ undefined, + createConstDeclarationList([ + createVariableDeclaration( + "_super", + createGeti(createSuper()) + ) + ]) + ); } export function inlineExpressions(expressions: Expression[]) { return reduceLeft(expressions, createComma); } + export function createExpressionFromEntityName(node: EntityName | Expression): Expression { + return isQualifiedName(node) + ? createPropertyAccess( + createExpressionFromEntityName(node.left), + cloneNode(node.right) + ) + : cloneNode(node); + } + + + // Utilities + function coerceIdentifier(value: string | Identifier) { if (typeof value === "string") { return createIdentifier(value); @@ -264,6 +801,24 @@ namespace ts { } } + function coerceBindingName(value: string | BindingName) { + if (typeof value === "string") { + return createIdentifier(value); + } + else { + return value; + } + } + + function coercePropertyName(value: string | PropertyName) { + if (typeof value === "string") { + return createIdentifier(value); + } + else { + return value; + } + } + function coerceExpression(value: string | number | boolean | Expression): Expression { if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { return createLiteral(value); @@ -291,7 +846,7 @@ namespace ts { } return needsParenthesesForBinary(operand, operator, side) - ? parenthesizeExpression(operand) + ? createParen(operand) : operand; } @@ -343,6 +898,42 @@ namespace ts { return expr; } - return parenthesizeExpression(expr); + return createParen(expr); + } + + function parenthesizeForUnary(operand: Expression) { + if (isUnaryExpression(operand)) { + return operand; + } + + return createParen(operand); + } + + + export function startOnNewLine(node: T): T { + (node).startsOnNewLine = true; + return node; + } + + export function setOriginalNode(node: T, original: Node): T { + node.original = original; + return node; + } + + export function setTextRange(node: T, location: TextRange): T { + if (location) { + node.pos = location.pos; + node.end = location.end; + } + return node; + } + + export function setNodeFlags(node: T, flags: NodeFlags): T { + node.flags = flags; + return node; + } + + export function getSynthesizedNode(node: T): T { + return nodeIsSynthesized(node) ? node : cloneNode(node, /*location*/ undefined, node.flags, /*parent*/ undefined, /*original*/ node); } } \ No newline at end of file diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 150ddb6a918c2..51129506e89bf 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -75,6 +75,15 @@ function __export(m) { } })`; + const superHelper = ` +const _super = name => super[name];`; + + const advancedSuperHelper = ` +const _super = (function (geti, seti) { + const cache = Object.create(null); + return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); +})(name => super[name], (name, value) => super[name] = value);`; + const compilerOptions = host.getCompilerOptions(); const languageVersion = getLanguageVersion(compilerOptions); const moduleKind = getModuleKind(compilerOptions); @@ -122,8 +131,12 @@ function __export(m) { let startLexicalEnvironment: () => void; let endLexicalEnvironment: () => Statement[]; let getNodeEmitFlags: (node: Node) => NodeEmitFlags; + let isExpressionSubstitutionEnabled: (node: Node) => boolean; + let isEmitNotificationEnabled: (node: Node) => boolean; let expressionSubstitution: (node: Expression) => Expression; let identifierSubstitution: (node: Identifier) => Identifier; + let onBeforeEmitNode: (node: Node) => void; + let onAfterEmitNode: (node: Node) => void; let isUniqueName: (name: string) => boolean; let temporaryVariables: string[] = []; let tempFlags: TempFlags; @@ -177,8 +190,12 @@ function __export(m) { startLexicalEnvironment = undefined; endLexicalEnvironment = undefined; getNodeEmitFlags = undefined; + isExpressionSubstitutionEnabled = undefined; + isEmitNotificationEnabled = undefined; expressionSubstitution = undefined; identifierSubstitution = undefined; + onBeforeEmitNode = undefined; + onAfterEmitNode = undefined; isUniqueName = undefined; temporaryVariables = undefined; tempFlags = 0; @@ -196,8 +213,12 @@ function __export(m) { startLexicalEnvironment = context.startLexicalEnvironment; endLexicalEnvironment = context.endLexicalEnvironment; getNodeEmitFlags = context.getNodeEmitFlags; + isExpressionSubstitutionEnabled = context.isExpressionSubstitutionEnabled; + isEmitNotificationEnabled = context.isEmitNotificationEnabled; expressionSubstitution = context.expressionSubstitution; identifierSubstitution = context.identifierSubstitution; + onBeforeEmitNode = context.onBeforeEmitNode; + onAfterEmitNode = context.onAfterEmitNode; isUniqueName = context.isUniqueName; return printSourceFile; } @@ -213,6 +234,11 @@ function __export(m) { function emit(node: Node) { if (node) { + const adviseOnEmit = isEmitNotificationEnabled(node); + if (adviseOnEmit && onBeforeEmitNode) { + onBeforeEmitNode(node); + } + const leadingComments = getLeadingCommentsToEmit(node); const trailingComments = getTrailingCommentsToEmit(node); emitLeadingComments(node, leadingComments); @@ -220,6 +246,10 @@ function __export(m) { emitWorker(node); emitEnd(node); emitTrailingComments(node, trailingComments); + + if (adviseOnEmit && onAfterEmitNode) { + onAfterEmitNode(node); + } } } @@ -234,7 +264,11 @@ function __export(m) { // Identifiers case SyntaxKind.Identifier: - return emitIdentifier(node, identifierSubstitution); + if (tryEmitSubstitute(node, identifierSubstitution)) { + return; + } + + return emitIdentifier(node); // Reserved words case SyntaxKind.ConstKeyword: @@ -486,6 +520,10 @@ function __export(m) { function emitExpressionWorker(node: Node) { const kind = node.kind; + if (isExpressionSubstitutionEnabled(node) && tryEmitSubstitute(node, expressionSubstitution)) { + return; + } + switch (kind) { // Literals case SyntaxKind.NumericLiteral: @@ -496,16 +534,15 @@ function __export(m) { // Identifiers case SyntaxKind.Identifier: - return emitIdentifier(node, expressionSubstitution); + return emitIdentifier(node); // Reserved words case SyntaxKind.FalseKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.TrueKeyword: - return writeTokenNode(node); case SyntaxKind.ThisKeyword: - return emitThisKeyword(node); + return writeTokenNode(node); // Expressions case SyntaxKind.ArrayLiteralExpression: @@ -597,11 +634,8 @@ function __export(m) { // Identifiers // - function emitIdentifier(node: Identifier, substitution: (node: Node) => Node) { - if (tryEmitSubstitute(node, substitution)) { - return; - } - else if (node.text === undefined) { + function emitIdentifier(node: Identifier) { + if (node.text === undefined) { // Emit a temporary variable name for this node. const nodeId = getOriginalNodeId(node); const text = temporaryVariables[nodeId] || (temporaryVariables[nodeId] = makeTempVariableName(tempKindToFlags(node.tempKind))); @@ -620,15 +654,6 @@ function __export(m) { } } - function emitThisKeyword(node: PrimaryExpression) { - if (tryEmitSubstitute(node, expressionSubstitution)) { - return; - } - else { - writeTokenNode(node); - } - } - // // Names // @@ -1051,19 +1076,11 @@ function __export(m) { } function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { - if (tryEmitSubstitute(node, expressionSubstitution)) { - return; - } - emitExpression(node.operand); writeToken(node.operator); } function emitBinaryExpression(node: BinaryExpression) { - if (tryEmitSubstitute(node, expressionSubstitution)) { - return; - } - const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; const indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); @@ -1390,9 +1407,12 @@ function __export(m) { function emitBlockFunctionBody(parentNode: Node, body: Block) { // Emit all the prologue directives (like "use strict"). + increaseIndent(); const statements = body.statements; - const statementOffset = emitPrologueDirectives(statements, /*startWithNewLine*/ true, /*indented*/ true); + const statementOffset = emitPrologueDirectives(statements, /*startWithNewLine*/ true); const helpersEmitted = emitHelpers(body); + decreaseIndent(); + if (statementOffset === 0 && !helpersEmitted && shouldEmitBlockFunctionBodyOnSingleLine(parentNode, body)) { emitList(body, statements, ListFormat.SingleLineFunctionBodyStatements); } @@ -1688,6 +1708,7 @@ function __export(m) { function emitHeritageClause(node: HeritageClause) { emitStart(node); + write(" "); writeToken(node.token); write(" "); emitList(node, node.types, ListFormat.HeritageClauseTypes); @@ -1783,8 +1804,7 @@ function __export(m) { } } - function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean, indented?: boolean) { - increaseIndentIf(indented); + function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean) { for (let i = 0; i < statements.length; i++) { if (isPrologueDirective(statements[i])) { if (startWithNewLine || i > 0) { @@ -1794,24 +1814,32 @@ function __export(m) { } else { // return index of the first non prologue directive - decreaseIndentIf(indented); return i; } } - decreaseIndentIf(indented); return statements.length; } function emitHelpers(node: Node) { const emitFlags = getNodeEmitFlags(node); let helpersEmitted = false; - if (emitFlags & NodeEmitFlags.EmitHelpers) { + if (emitFlags & NodeEmitFlags.EmitEmitHelpers) { helpersEmitted = emitEmitHelpers(currentSourceFile); } if (emitFlags & NodeEmitFlags.EmitExportStar) { - emitExportStarHelper(); + writeLines(exportStarHelper); + helpersEmitted = true; + } + + if (emitFlags & NodeEmitFlags.EmitSuperHelper) { + writeLines(superHelper); + helpersEmitted = true; + } + + if (emitFlags & NodeEmitFlags.EmitAdvancedSuperHelper) { + writeLines(advancedSuperHelper); helpersEmitted = true; } @@ -1861,10 +1889,6 @@ function __export(m) { return helpersEmitted; } - function emitExportStarHelper() { - writeLines(exportStarHelper); - } - function writeLines(text: string): void { const lines = text.split(/\r\n|\r|\n/g); for (let i = 0; i < lines.length; i++) { diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 3e6e1dcf6e59d..7552efddb2118 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -56,6 +56,9 @@ namespace ts { const nodeEmitFlags: NodeEmitFlags[] = []; const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; + const enabledExpressionSubstitutions = new Array(SyntaxKind.Count); + const enabledEmitNotifications = new Array(SyntaxKind.Count); + let lexicalEnvironmentStackOffset = 0; let hoistedVariableDeclarations: VariableDeclaration[]; let hoistedFunctionDeclarations: FunctionDeclaration[]; @@ -75,7 +78,11 @@ namespace ts { hoistVariableDeclaration, hoistFunctionDeclaration, startLexicalEnvironment, - endLexicalEnvironment + endLexicalEnvironment, + enableExpressionSubstitution, + isExpressionSubstitutionEnabled, + enableEmitNotification, + isEmitNotificationEnabled, }; // Chain together and initialize each transformer. @@ -100,6 +107,23 @@ namespace ts { return visited; } + function enableExpressionSubstitution(kind: SyntaxKind) { + enabledExpressionSubstitutions[kind] = true; + } + + function isExpressionSubstitutionEnabled(node: Node) { + return enabledExpressionSubstitutions[node.kind]; + } + + function enableEmitNotification(kind: SyntaxKind) { + enabledEmitNotifications[kind] = true; + } + + function isEmitNotificationEnabled(node: Node) { + return enabledEmitNotifications[node.kind] + || (getNodeEmitFlags(node) & NodeEmitFlags.AdviseOnEmitNode) !== 0; + } + /** * Gets flags that control emit behavior of a node. */ @@ -207,7 +231,7 @@ namespace ts { case SyntaxKind.ClassExpression: return generateNameForClassExpression(); default: - return createTempVariable(TempVariableKind.Auto); + return createTempVariable(); } } @@ -289,6 +313,7 @@ namespace ts { if (hoistedVariableDeclarations) { statements.push( createVariableStatement( + /*modifiers*/ undefined, createVariableDeclarationList(hoistedVariableDeclarations) ) ); diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 19c25e5699f15..c21d81ac4d517 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -10,8 +10,14 @@ namespace ts { * @param needsValue Indicates whether the value from the right-hand-side of the * destructuring assignment is needed as part of a larger expression. * @param recordTempVariable A callback used to record new temporary variables. + * @param visitor An optional visitor to use to visit expressions. */ - export function flattenDestructuringAssignment(node: BinaryExpression, needsValue: boolean, recordTempVariable: (node: Identifier) => void) { + export function flattenDestructuringAssignment( + node: BinaryExpression, + needsValue: boolean, + recordTempVariable: (node: Identifier) => void, + visitor?: (node: Node) => Node) { + let location: TextRange = node; let value = node.right; if (isEmptyObjectLiteralOrArrayLiteral(node.left)) { @@ -31,7 +37,7 @@ namespace ts { location = node.right; } - flattenDestructuring(node, value, location, emitAssignment, emitTempVariableAssignment); + flattenDestructuring(node, value, location, emitAssignment, emitTempVariableAssignment, visitor); if (needsValue) { expressions.push(value); @@ -64,11 +70,12 @@ namespace ts { * * @param node The ParameterDeclaration to flatten. * @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) { + export function flattenParameterDestructuring(node: ParameterDeclaration, value: Expression, visitor?: (node: Node) => Node) { const declarations: VariableDeclaration[] = []; - flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment); + flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment, visitor); return declarations; @@ -94,11 +101,12 @@ namespace ts { * * @param node The VariableDeclaration to flatten. * @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) { + export function flattenVariableDestructuring(node: VariableDeclaration, value?: Expression, visitor?: (node: Node) => Node) { const declarations: VariableDeclaration[] = []; - flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment); + flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment, visitor); return declarations; @@ -129,8 +137,15 @@ namespace ts { * * @param node The VariableDeclaration to flatten. * @param recordTempVariable A callback used to record new temporary variables. + * @param nameSubstitution An optional callback used to substitute binding names. + * @param visitor An optional visitor to use to visit expressions. */ - export function flattenVariableDestructuringToExpression(node: VariableDeclaration, recordTempVariable: (name: Identifier) => void) { + export function flattenVariableDestructuringToExpression( + node: VariableDeclaration, + recordTempVariable: (name: Identifier) => void, + nameSubstitution?: (name: Identifier) => Expression, + visitor?: (node: Node) => Node) { + const pendingAssignments: Expression[] = []; flattenDestructuring(node, /*value*/ undefined, node, emitAssignment, emitTempVariableAssignment); @@ -140,6 +155,18 @@ namespace ts { return expression; function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) { + const left = nameSubstitution && nameSubstitution(name) || name; + emitPendingAssignment(left, value, location, original); + } + + function emitTempVariableAssignment(value: Expression, location: TextRange) { + const name = createTempVariable(); + recordTempVariable(name); + emitPendingAssignment(name, value, location, /*original*/ undefined); + return name; + } + + function emitPendingAssignment(name: Expression, value: Expression, location: TextRange, original: Node) { const expression = createAssignment(name, value, location); if (isSimpleExpression(value)) { (expression).disableSourceMap = true; @@ -147,13 +174,7 @@ namespace ts { expression.original = original; pendingAssignments.push(expression); - } - - function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(); - recordTempVariable(name); - emitAssignment(name, value, location, /*original*/ undefined); - return name; + return expression; } } @@ -162,7 +183,12 @@ namespace ts { value: Expression, location: TextRange, emitAssignment: (name: Identifier, value: Expression, location: TextRange, original: Node) => void, - emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier) { + emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier, + visitor?: (node: Node) => Node) { + if (value && visitor) { + value = visitNode(value, visitor, isExpression); + } + if (isBinaryExpression(root)) { emitDestructuringAssignment(root.left, value, location) } @@ -174,14 +200,22 @@ namespace ts { // When emitting target = value use source map node to highlight, including any temporary assignments needed for this let target: Expression; if (isShortHandPropertyAssignment(bindingTarget)) { - if (bindingTarget.objectAssignmentInitializer) { - value = createDefaultValueCheck(value, bindingTarget.objectAssignmentInitializer, location); + const initializer = visitor + ? visitNode(bindingTarget.objectAssignmentInitializer, visitor, isExpression) + : bindingTarget.objectAssignmentInitializer; + + if (initializer) { + value = createDefaultValueCheck(value, initializer, location); } target = bindingTarget.name; } else if (isBinaryExpression(bindingTarget) && bindingTarget.operatorToken.kind === SyntaxKind.EqualsToken) { - value = createDefaultValueCheck(value, bindingTarget.right, location); + const initializer = visitor + ? visitNode(bindingTarget.right, visitor, isExpression) + : bindingTarget.right; + + value = createDefaultValueCheck(value, initializer, location); target = bindingTarget.left; } else { @@ -244,9 +278,10 @@ namespace ts { function emitBindingElement(target: BindingElement, value: Expression) { // Any temporary assignments needed to emit target = value should point to target - if (target.initializer) { + const initializer = visitor ? visitNode(target.initializer, visitor, isExpression) : target.initializer; + if (initializer) { // Combine value and initializer - value = value ? createDefaultValueCheck(value, target.initializer, target) : target.initializer; + value = value ? createDefaultValueCheck(value, initializer, target) : initializer; } else if (!value) { // Use 'void 0' in absence of value and initializer diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 0156bb5ff8434..38e7a639b1d71 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1,30 +1,2737 @@ /// /// +/// /*@internal*/ namespace ts { + type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration; + // TODO(rbuckton): TS->ES7 transformer export function transformTypeScript(context: TransformationContext) { + const { + nodeHasGeneratedName, + getGeneratedNameForNode, + makeUniqueName, + setNodeEmitFlags, + startLexicalEnvironment, + endLexicalEnvironment, + hoistVariableDeclaration, + } = context; + + const resolver = context.getEmitResolver(); + const compilerOptions = context.getCompilerOptions(); + const languageVersion = getLanguageVersion(compilerOptions); + const decoratedClassAliases: Map = {}; + const currentDecoratedClassAliases: Map = {}; + const previousExpressionSubstitution = context.expressionSubstitution; + const previousOnBeforeEmitNode = context.onBeforeEmitNode; + const previousOnAfterEmitNode = context.onAfterEmitNode; + context.enableExpressionSubstitution(SyntaxKind.Identifier); + context.expressionSubstitution = substituteExpression; + context.onBeforeEmitNode = onBeforeEmitNode; + context.onAfterEmitNode = onAfterEmitNode; + + let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false; + let currentSourceFile: SourceFile; + let currentNamespace: ModuleDeclaration; + let currentNamespaceLocalName: Identifier; + let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; + let currentParent: Node; + let currentNode: Node; + let isRightmostExpression: boolean; + let superContainerStack: SuperContainer[]; + return transformSourceFile; function transformSourceFile(node: SourceFile) { - return visitEachChild(node, visitor, context); + currentSourceFile = node; + node = visitEachChild(node, visitor, context); + setNodeEmitFlags(node, NodeEmitFlags.EmitEmitHelpers); + currentSourceFile = undefined; + return node; + } + + /** + * Visits a node, saving and restoring state variables on the stack. + * + * @param node The node to visit. + */ + function visitWithStack(node: Node, visitor: (node: Node) => Node): Node { + const savedCurrentNamespace = currentNamespace; + const savedCurrentScope = currentScope; + const savedCurrentParent = currentParent; + const savedCurrentNode = currentNode; + const savedIsRightmostExpression = isRightmostExpression; + onBeforeVisitNode(node); + node = visitor(node); + currentNamespace = savedCurrentNamespace; + currentScope = savedCurrentScope; + currentParent = savedCurrentParent; + currentNode = savedCurrentNode; + isRightmostExpression = savedIsRightmostExpression; + return node; } - function visitor(node: Node) { + /** + * General-purpose node visitor. + * + * @param node The node to visit. + */ + function visitor(node: Node): Node { + return visitWithStack(node, visitorWorker); + } + + /** + * Visits and possibly transforms any node. + * + * @param node The node to visit. + */ + function visitorWorker(node: Node): Node { if (node.transformFlags & TransformFlags.TypeScript) { - return visitorWorker(node); + // This node is explicitly marked as TypeScript, so we should transform the node. + node = visitTypeScript(node); + } + else if (node.transformFlags & TransformFlags.ContainsTypeScript) { + // This node contains TypeScript, so we should visit its children. + node = visitEachChild(node, visitor, context); + } + + return node; + } + + /** + * Specialized visitor that visits the immediate children of a namespace. + * + * @param node The node to visit. + */ + function namespaceElementVisitor(node: Node): Node { + return visitWithStack(node, namespaceElementVisitorWorker); + } + + /** + * Specialized visitor that visits the immediate children of a namespace. + * + * @param node The node to visit. + */ + function namespaceElementVisitorWorker(node: Node): Node { + if (node.transformFlags & TransformFlags.TypeScript + || node.flags & NodeFlags.Export) { + // This node is explicitly marked as TypeScript, or is exported at the namespace + // level, so we should transform the node. + node = visitTypeScript(node); } else if (node.transformFlags & TransformFlags.ContainsTypeScript) { - return visitEachChild(node, visitor, context); + // This node contains TypeScript, so we should visit its children. + node = visitEachChild(node, visitor, context); + } + + return node; + } + + /** + * Specialized visitor that visits the immediate children of a class with TypeScript syntax. + * + * @param node The node to visit. + */ + function classElementVisitor(node: Node) { + return visitWithStack(node, classElementVisitorWorker); + } + + /** + * Specialized visitor that visits the immediate children of a class with TypeScript syntax. + * + * @param node The node to visit. + */ + function classElementVisitorWorker(node: Node) { + if (node.kind === SyntaxKind.Constructor) { + // TypeScript constructors are elided. + return undefined; + } + + return visitorWorker(node); + } + + function visitTypeScript(node: Node): Node { + // TypeScript ambient declarations are elided. + if (node.flags & NodeFlags.Ambient) { + return; + } + + switch (node.kind) { + case SyntaxKind.ExportKeyword: + case SyntaxKind.DefaultKeyword: + // ES6 export and default modifiers are elided when inside a namespace. + return currentNamespace ? undefined : node; + + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.AbstractKeyword: + case SyntaxKind.AsyncKeyword: + case SyntaxKind.ConstKeyword: + case SyntaxKind.DeclareKeyword: + // TypeScript accessibility modifiers are elided. + + case SyntaxKind.ArrayType: + case SyntaxKind.TupleType: + case SyntaxKind.TypeLiteral: + case SyntaxKind.TypePredicate: + case SyntaxKind.TypeParameter: + case SyntaxKind.AnyKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.ConstructorType: + case SyntaxKind.FunctionType: + case SyntaxKind.TypeQuery: + case SyntaxKind.TypeReference: + case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: + case SyntaxKind.StringLiteralType: + case SyntaxKind.ThisType: + // TypeScript type nodes are elided. + + case SyntaxKind.IndexSignature: + // TypeScript index signatures are elided. + + case SyntaxKind.Decorator: + // TypeScript decorators are elided. They will be emitted as part of transformClassDeclaration. + + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + // TypeScript type-only declarations are elided + + case SyntaxKind.PropertyDeclaration: + // TypeScript property declarations are elided. + + 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 undefined; + + case SyntaxKind.ClassDeclaration: + // This is a class declaration with TypeScript syntax extensions. + // + // TypeScript class syntax extensions include: + // - decorators + // - optional `implements` heritage clause + // - parameter property assignments in the constructor + // - property declarations + // - index signatures + // - method overload signatures + // - async methods + return visitClassDeclaration(node); + + case SyntaxKind.ClassExpression: + // This is a class expression with TypeScript syntax extensions. + // + // TypeScript class syntax extensions include: + // - decorators + // - optional `implements` heritage clause + // - parameter property assignments in the constructor + // - property declarations + // - index signatures + // - method overload signatures + // - async methods + return visitClassExpression(node); + + case SyntaxKind.HeritageClause: + // This is a heritage clause with TypeScript syntax extensions. + // + // TypeScript heritage clause extensions include: + // - `implements` clause + return visitHeritageClause(node); + + case SyntaxKind.ExpressionWithTypeArguments: + // TypeScript supports type arguments on an expression in an `extends` heritage clause. + return visitExpressionWithTypeArguments(node); + + case SyntaxKind.MethodDeclaration: + // TypeScript method declarations may be 'async', and may have decorators, modifiers + // or type annotations. + return visitMethodDeclaration(node); + + case SyntaxKind.GetAccessor: + // Get Accessors can have TypeScript modifiers, decorators, and type annotations. + return visitGetAccessor(node); + + case SyntaxKind.SetAccessor: + // Set Accessors can have TypeScript modifiers, decorators, and type annotations. + return visitSetAccessor(node); + + case SyntaxKind.FunctionDeclaration: + // TypeScript function declarations may be 'async' + return visitFunctionDeclaration(node); + + case SyntaxKind.FunctionExpression: + // TypeScript function expressions may be 'async' + return visitFunctionExpression(node); + + case SyntaxKind.ArrowFunction: + // TypeScript arrow functions may be 'async' + return visitArrowFunction(node); + + case SyntaxKind.Parameter: + // This is a parameter declaration with TypeScript syntax extensions. + // + // TypeScript parameter declaration syntax extensions include: + // - decorators + // - accessibility modifiers + // - the question mark (?) token for optional parameters + // - type annotations + return visitParameter(node); + + case SyntaxKind.ParenthesizedExpression: + // ParenthesizedExpressions are TypeScript if their expression is a + // TypeAssertion or AsExpression + return visitParenthesizedExpression(node); + + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.AsExpression: + // TypeScript type assertions are removed, but their subtrees are preserved. + return visitNode((node).expression, visitor, isExpression); + + case SyntaxKind.EnumDeclaration: + // TypeScript enum declarations do not exist in ES6 and must be rewritten. + return visitEnumDeclaration(node); + + case SyntaxKind.AwaitExpression: + // TypeScript 'await' expressions must be transformed. + return visitAwaitExpression(node); + + case SyntaxKind.VariableStatement: + // TypeScript namespace exports for variable statements must be transformed. + return visitVariableStatement(node); + + case SyntaxKind.ModuleDeclaration: + // TypeScript namespace declarations must be transformed. + return visitModuleDeclaration(node); + + case SyntaxKind.ImportEqualsDeclaration: + // TypeScript namespace or external module import. + return visitImportEqualsDeclaration(node); + + default: + Debug.fail("Unexpected node."); + break; + } + } + + /** + * Performs actions that should always occur immediately before visiting a node. + * + * @param node The node to visit. + */ + function onBeforeVisitNode(node: Node) { + currentParent = currentNode; + currentNode = node; + switch (node.kind) { + case SyntaxKind.SourceFile: + case SyntaxKind.CaseBlock: + case SyntaxKind.ModuleBlock: + case SyntaxKind.Block: + currentScope = node; + break; + } + + // Keep track of whether this is the right-most expression of an expression tree. + // This is used to determine whether to parenthesize an `await` expression transformed + // into a `yield` expression. + isRightmostExpression = currentParent && isRightmost(currentParent, currentNode, isRightmostExpression); + } + + /** + * Transforms a class declaration with TypeScript syntax into compatible ES6. + * + * This function will only be called when one of the following conditions are met: + * - The class has decorators. + * - The class has property declarations with initializers. + * - The class contains a constructor that contains parameters with accessibility modifiers. + * - The class is an export in a TypeScript namespace. + * + * @param node The node to transform. + */ + function visitClassDeclaration(node: ClassDeclaration): NodeArrayNode { + const staticProperties = getInitializedProperties(node, /*isStatic*/ true); + const statements: Statement[] = []; + const modifiers = visitNodes(node.modifiers, visitor, isModifier); + const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); + const members = transformClassMembers(node, heritageClauses !== undefined); + let decoratedClassAlias: Identifier; + + // emit name if + // - node has a name + // - node has static initializers + // + let name = node.name; + if (!name && staticProperties.length > 0) { + name = getGeneratedNameForNode(node); + } + + if (node.decorators) { + // When we emit an ES6 class that has a class decorator, we must tailor the + // emit to certain specific cases. + // + // In the simplest case, we emit the class declaration as a let declaration, and + // evaluate decorators after the close of the class body: + // + // [Example 1] + // --------------------------------------------------------------------- + // TypeScript | Javascript + // --------------------------------------------------------------------- + // @dec | let C = class C { + // class C { | } + // } | C = __decorate([dec], C); + // --------------------------------------------------------------------- + // @dec | let C = class C { + // export class C { | } + // } | C = __decorate([dec], C); + // | export { C }; + // --------------------------------------------------------------------- + // + // If a class declaration contains a reference to itself *inside* of the class body, + // this introduces two bindings to the class: One outside of the class body, and one + // inside of the class body. If we apply decorators as in [Example 1] above, there + // is the possibility that the decorator `dec` will return a new value for the + // constructor, which would result in the binding inside of the class no longer + // pointing to the same reference as the binding outside of the class. + // + // As a result, we must instead rewrite all references to the class *inside* of the + // class body to instead point to a local temporary alias for the class: + // + // [Example 2] + // --------------------------------------------------------------------- + // TypeScript | Javascript + // --------------------------------------------------------------------- + // @dec | let C_1; + // class C { | let C = C_1 = class C { + // static x() { return C.y; } | static x() { return C_1.y; } + // static y = 1; | } + // } | C.y = 1; + // | C = C_1 = __decorate([dec], C); + // --------------------------------------------------------------------- + // @dec | let C_1; + // export class C { | let C = C_1 = class C { + // static x() { return C.y; } | static x() { return C_1.y; } + // static y = 1; | } + // } | C.y = 1; + // | C = C_1 = __decorate([dec], C); + // | export { C }; + // --------------------------------------------------------------------- + // + // If a class declaration is the default export of a module, we instead emit + // the export after the decorated declaration: + // + // [Example 3] + // --------------------------------------------------------------------- + // TypeScript | Javascript + // --------------------------------------------------------------------- + // @dec | let default_1 = class { + // export default class { | } + // } | default_1 = __decorate([dec], default_1); + // | export default default_1; + // --------------------------------------------------------------------- + // @dec | let C = class C { + // export default class C { | } + // } | C = __decorate([dec], C); + // | export default C; + // --------------------------------------------------------------------- + // + // If the class declaration is the default export and a reference to itself + // inside of the class body, we must emit both an alias for the class *and* + // move the export after the declaration: + // + // [Example 4] + // --------------------------------------------------------------------- + // TypeScript | Javascript + // --------------------------------------------------------------------- + // @dec | let C_1; + // export default class C { | let C = C_1 = class C { + // static x() { return C.y; } | static x() { return C_1.y; } + // static y = 1; | } + // } | C.y = 1; + // | C = C_1 = __decorate([dec], C); + // | export default C; + // --------------------------------------------------------------------- + // + + // class ${name} ${heritageClauses} { + // ${members} + // } + let classExpression: Expression = setOriginalNode( + createClassExpression( + name, + heritageClauses, + members, + /*location*/ node + ), + node + ); + + // Record an alias to avoid class double-binding. + if (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.ClassWithBodyScopedClassBinding) { + decoratedClassAlias = makeUniqueName(node.name ? node.name.text : "default"); + decoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAlias; + + // We emit the class alias as a `let` declaration here so that it has the same + // TDZ as the class. + // let ${decoratedClassAlias}; + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration(decoratedClassAlias) + ], + /*location*/ undefined, + NodeFlags.Let) + ) + ); + + // ${decoratedClassAlias} = ${classExpression} + classExpression = createAssignment( + cloneNode(decoratedClassAlias), + classExpression, + /*location*/ node); + } + + // let ${name} = ${classExpression}; + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + name, + classExpression + ) + ], + /*location*/ undefined, + NodeFlags.Let) + ) + ); } else { - return node; + // ${modifiers} class ${name} ${heritageClauses} { + // ${members} + // } + addNode(statements, + setOriginalNode( + createClassDeclaration( + modifiers, + name, + heritageClauses, + members, + /*location*/ node + ), + node + ) + ); + } + + // Emit static property assignment. Because classDeclaration is lexically evaluated, + // it is safe to emit static property assignment after classDeclaration + // From ES6 specification: + // HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using + // a lexical declaration such as a LexicalDeclaration or a ClassDeclaration. + addNodes(statements, generateInitializedPropertyStatements(node, staticProperties, name)); + + // Write any decorators of the node. + addNodes(statements, generateClassElementDecorationStatements(node, /*isStatic*/ false)); + addNodes(statements, generateClassElementDecorationStatements(node, /*isStatic*/ true)); + addNode(statements, generateConstructorDecorationStatement(node, decoratedClassAlias)) + + // If the class is exported as part of a TypeScript namespace, emit the namespace export. + // Otherwise, if the class was exported at the top level and was decorated, emit an export + // declaration or export default for the class. + if (isNamespaceExport(node)) { + addNode(statements, createNamespaceExport(name, name)); + } + else if (node.decorators) { + if (isDefaultExternalModuleExport(node)) { + addNode(statements, createExportDefault(name)); + } + else if (isNamedExternalModuleExport(node)) { + addNode(statements, createModuleExport(name)); + } + } + + return createNodeArrayNode(statements); + } + + /** + * Transforms a class expression with TypeScript syntax into compatible ES6. + * + * This function will only be called when one of the following conditions are met: + * - The class has property declarations with initializers. + * - The class contains a constructor that contains parameters with accessibility modifiers. + * + * @param node The node to transform. + */ + function visitClassExpression(node: ClassExpression): LeftHandSideExpression { + const staticProperties = getInitializedProperties(node, /*isStatic*/ true); + const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); + const members = transformClassMembers(node, heritageClauses !== undefined); + + // emit name if + // - node has a name + // - node has static initializers + // + let name = node.name; + if (!name && staticProperties.length > 0) { + name = getGeneratedNameForNode(node); } + + const classExpression = setOriginalNode( + createClassExpression( + name, + heritageClauses, + members, + /*location*/ node + ), + node + ); + + if (staticProperties.length > 0) { + const expressions: Expression[] = []; + const temp = createTempVariable(); + hoistVariableDeclaration(temp); + addNode(expressions, createAssignment(temp, classExpression)); + addNodes(expressions, generateInitializedPropertyExpressions(node, staticProperties, temp)); + addNode(expressions, temp); + return createParen(inlineExpressions(expressions)); + } + + return classExpression; } - function visitorWorker(node: Node): Node { + /** + * Transforms the members of a class. + * + * @param node The current class. + * @param hasExtendsClause A value indicating whether the class has an extends clause. + */ + function transformClassMembers(node: ClassDeclaration | ClassExpression, hasExtendsClause: boolean) { + const members: ClassElement[] = []; + addNode(members, transformConstructor(node, hasExtendsClause)); + addNodes(members, visitNodes(node.members, classElementVisitor, isClassElement)); + return members; + } + + /** + * Transforms (or creates) a constructor for a class. + * + * @param node The current class. + * @param hasExtendsClause A value indicating whether the class has an extends clause. + */ + function transformConstructor(node: ClassDeclaration | ClassExpression, hasExtendsClause: boolean) { + // Check if we have property assignment inside class declaration. + // If there is a property assignment, we need to emit constructor whether users define it or not + // If there is no property assignment, we can omit constructor if users do not define it + const hasInstancePropertyWithInitializer = forEach(node.members, isInstanceInitializedProperty); + const hasParameterPropertyAssignments = node.transformFlags & TransformFlags.ContainsParameterPropertyAssignments; + const constructor = getFirstConstructorWithBody(node); + + // If the class does not contain nodes that require a synthesized constructor, + // accept the current constructor if it exists. + if (!hasInstancePropertyWithInitializer && !hasParameterPropertyAssignments) { + return visitEachChild(constructor, visitor, context); + } + + const parameters = transformConstructorParameters(constructor, hasExtendsClause); + const body = transformConstructorBody(node, constructor, hasExtendsClause, parameters); + + // constructor(${parameters}) { + // ${body} + // } + return startOnNewLine( + setOriginalNode( + createConstructor( + parameters, + body, + /*location*/ constructor + ), + constructor + ) + ); + } + + /** + * Transforms (or creates) the parameters for the constructor of a class with + * parameter property assignments or instance property initializers. + * + * @param constructor The constructor declaration. + * @param hasExtendsClause A value indicating whether the class has an extends clause. + */ + function transformConstructorParameters(constructor: ConstructorDeclaration, hasExtendsClause: boolean) { + return constructor + ? visitNodes(constructor.parameters, visitor, isParameter) + : hasExtendsClause ? [createRestParameter(makeUniqueName("args"))] : []; + } + + /** + * Transforms (or creates) a constructor body for a class with parameter property + * assignments or instance property initializers. + * + * @param node The current class. + * @param constructor The current class constructor. + * @param hasExtendsClause A value indicating whether the class has an extends clause. + * @param parameters The transformed parameters for the constructor. + */ + function transformConstructorBody(node: ClassExpression | ClassDeclaration, constructor: ConstructorDeclaration, hasExtendsClause: boolean, parameters: ParameterDeclaration[]) { + let hasSuperCall = false; + const statements: Statement[] = []; + + // The body of a constructor is a new lexical environment + startLexicalEnvironment(); + + if (constructor) { + const superCall = visitNode(findInitialSuperCall(constructor), visitor, isStatement); + if (superCall) { + // Adds the existing super call as the first line of the constructor. + addNode(statements, superCall); + hasSuperCall = true; + } + + // Add parameters with property assignments. Transforms this: + // + // constructor (public x, public y) { + // } + // + // Into this: + // + // constructor (x, y) { + // this.x = x; + // this.y = y; + // } + // + const propertyAssignments = getParametersWithPropertyAssignments(constructor); + addNodes(statements, map(propertyAssignments, transformParameterWithPropertyAssignment)); + } + else if (hasExtendsClause) { + Debug.assert(parameters.length === 1 && isIdentifier(parameters[0].name)); + + // Add a synthetic `super` call: + // + // super(...args); + // + addNode(statements, + createStatement( + createCall( + createSuper(), + [createSpread(parameters[0].name)] + ) + ) + ); + } + + // Add the property initializers. Transforms this: + // + // public x = 1; + // + // Into this: + // + // constructor() { + // this.x = 1; + // } + // + const properties = getInitializedProperties(node, /*isStatic*/ false); + addNodes(statements, generateInitializedPropertyStatements(node, properties, createThis())); + + if (constructor) { + // The class already had a constructor, so we should add the existing statements, skipping the initial super call. + addNodes(statements, visitNodes(constructor.body.statements, visitor, isStatement, hasSuperCall ? 1 : 0)); + } + + // End the lexical environment. + addNodes(statements, endLexicalEnvironment()); + return createBlock(statements); + } + + /** + * Finds the initial super-call for a constructor. + * + * @param ctor The constructor node. + */ + function findInitialSuperCall(ctor: ConstructorDeclaration): ExpressionStatement { + if (ctor.body) { + const statements = ctor.body.statements; + const statement = statements.length ? statements[0] : undefined; + if (statement.kind === SyntaxKind.ExpressionStatement) { + const expression = (statement).expression; + if (expression.kind === SyntaxKind.CallExpression) { + if ((expression).expression.kind === SyntaxKind.SuperKeyword) { + return statement; + } + } + } + } + + return undefined; + } + + /** + * Gets all parameters of a constructor that should be transformed into property assignments. + * + * @param node The constructor node. + */ + function getParametersWithPropertyAssignments(node: ConstructorDeclaration): ParameterDeclaration[] { + return filter(node.parameters, isParameterWithPropertyAssignment); + } + + /** + * Determines whether a parameter should be transformed into a property assignment. + * + * @param parameter The parameter node. + */ + function isParameterWithPropertyAssignment(parameter: ParameterDeclaration) { + return parameter.flags & NodeFlags.AccessibilityModifier + && isIdentifier(parameter.name); + } + + /** + * Transforms a parameter into a property assignment statement. + * + * @param node The parameter declaration. + */ + function transformParameterWithPropertyAssignment(node: ParameterDeclaration) { + Debug.assert(isIdentifier(node.name)); + + const name = cloneNode(node.name); + return startOnNewLine( + createStatement( + createAssignment( + createPropertyAccess(createThis(), name), + name + ) + ) + ); + } + + /** + * Gets all property declarations with initializers on either the static or instance side of a class. + * + * @param node The class node. + * @param isStatic A value indicating whether to get properties from the static or instance side of the class. + */ + function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): PropertyDeclaration[] { + return filter(node.members, isStatic ? isStaticInitializedProperty : isInstanceInitializedProperty); + } + + /** + * Gets a value indicating whether a class element is a static property declaration with an initializer. + * + * @param member The class element node. + */ + function isStaticInitializedProperty(member: ClassElement): member is PropertyDeclaration { + return isInitializedProperty(member, /*isStatic*/ true); + } + + /** + * Gets a value indicating whether a class element is an instance property declaration with an initializer. + * + * @param member The class element node. + */ + function isInstanceInitializedProperty(member: ClassElement): member is PropertyDeclaration { + return isInitializedProperty(member, /*isStatic*/ false); + } + + /** + * Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer. + * + * @param member The class element node. + * @param isStatic A value indicating whether the member should be a static or instance member. + */ + function isInitializedProperty(member: ClassElement, isStatic: boolean) { + return member.kind === SyntaxKind.PropertyDeclaration + && isStatic === ((member.flags & NodeFlags.Static) !== 0) + && (member).initializer !== undefined; + } + + /** + * Generates assignment statements for property initializers. + * + * @param node The class node. + * @param properties An array of property declarations to transform. + * @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); + } + + /** + * Generates assignment expressions for property initializers. + * + * @param node The class node. + * @param properties An array of property declarations to transform. + * @param receiver The receiver on which each property should be assigned. + */ + function generateInitializedPropertyExpressions(node: ClassExpression | ClassDeclaration, properties: PropertyDeclaration[], receiver: LeftHandSideExpression) { + const expressions: Expression[] = []; + for (const property of properties) { + expressions.push(transformInitializedProperty(node, property, receiver)); + } + return expressions; + } + + /** + * Transforms a property initializer into an assignment statement. + * + * @param node The class containing the property. + * @param property The property declaration. + * @param receiver The object receiving the property assignment. + */ + function transformInitializedProperty(node: ClassExpression | ClassDeclaration, property: PropertyDeclaration, receiver: LeftHandSideExpression) { + const propertyName = visitPropertyNameOfClassElement(property); + const initializer = visitNode(property.initializer, visitor, isExpression); + return createAssignment( + createMemberAccessForPropertyName(receiver, propertyName), + initializer + ); + } + + /** + * Gets either the static or instance members of a class that are decorated, or have + * parameters that are decorated. + * + * @param node The class containing the member. + * @param isStatic A value indicating whether to retrieve static or instance members of + * the class. + */ + function getDecoratedClassElements(node: ClassExpression | ClassDeclaration, isStatic: boolean): ClassElement[] { + return filter(node.members, isStatic ? isStaticDecoratedClassElement : isInstanceDecoratedClassElement); + } + + /** + * Determines whether a class member is a static member of a class that is decorated, or + * has parameters that are decorated. + * + * @param member The class member. + */ + function isStaticDecoratedClassElement(member: ClassElement) { + return isDecoratedClassElement(member, /*isStatic*/ true); + } + + /** + * Determines whether a class member is an instance member of a class that is decorated, + * or has parameters that are decorated. + * + * @param member The class member. + */ + function isInstanceDecoratedClassElement(member: ClassElement) { + return isDecoratedClassElement(member, /*isStatic*/ false); + } + + /** + * Determines whether a class member is either a static or an instance member of a class + * that is decorated, or has parameters that are decorated. + * + * @param member The class member. + */ + function isDecoratedClassElement(member: ClassElement, isStatic: boolean) { + return nodeOrChildIsDecorated(member) + && isStatic === ((member.flags & NodeFlags.Static) !== 0); + } + + /** + * A structure describing the decorators for a class element. + */ + interface AllDecorators { + decorators: Decorator[]; + parameters?: Decorator[][]; + } + + /** + * Gets an array of arrays of decorators for the parameters of a function-like node. + * The offset into the result array should correspond to the offset of the parameter. + * + * @param node The function-like node. + */ + function getDecoratorsOfParameters(node: FunctionLikeDeclaration) { + let decorators: Decorator[][]; + if (node) { + const parameters = node.parameters; + for (let i = 0; i < parameters.length; i++) { + const parameter = parameters[i]; + if (decorators || parameter.decorators) { + if (!decorators) { + decorators = new Array(parameters.length); + } + + decorators[i] = parameter.decorators; + } + } + } + + return decorators; + } + + /** + * Gets an AllDecorators object containing the decorators for the class and the decorators for the + * parameters of the constructor of the class. + * + * @param node The class node. + */ + function getAllDecoratorsOfConstructor(node: ClassExpression | ClassDeclaration): AllDecorators { + const decorators = node.decorators; + const parameters = getDecoratorsOfParameters(getFirstConstructorWithBody(node)); + if (!decorators && !parameters) { + return undefined; + } + + return { + decorators, + parameters + }; + } + + /** + * Gets an AllDecorators object containing the decorators for the member and its parameters. + * + * @param node The class node that contains the member. + * @param member The class member. + */ + function getAllDecoratorsOfClassElement(node: ClassExpression | ClassDeclaration, member: ClassElement): AllDecorators { + switch (member.kind) { + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return getAllDecoratorsOfAccessors(node, member); + + case SyntaxKind.MethodDeclaration: + return getAllDecoratorsOfMethod(member); + + case SyntaxKind.PropertyDeclaration: + return getAllDecoratorsOfProperty(member); + + default: + return undefined; + } + } + + /** + * Gets an AllDecorators object containing the decorators for the accessor and its parameters. + * + * @param node The class node that contains the accessor. + * @param accessor The class accessor member. + */ + function getAllDecoratorsOfAccessors(node: ClassExpression | ClassDeclaration, accessor: AccessorDeclaration): AllDecorators { + if (!accessor.body) { + return undefined; + } + + const { firstAccessor, secondAccessor, setAccessor } = getAllAccessorDeclarations(node.members, accessor); + if (accessor !== firstAccessor) { + return undefined; + } + + const decorators = firstAccessor.decorators || (secondAccessor && secondAccessor.decorators); + const parameters = getDecoratorsOfParameters(setAccessor); + if (!decorators && !parameters) { + return undefined; + } + + return { decorators, parameters }; + } + + /** + * Gets an AllDecorators object containing the decorators for the method and its parameters. + * + * @param method The class method member. + */ + function getAllDecoratorsOfMethod(method: MethodDeclaration): AllDecorators { + if (!method.body) { + return undefined; + } + + const decorators = method.decorators; + const parameters = getDecoratorsOfParameters(method); + if (!decorators && !parameters) { + return undefined; + } + + return { decorators, parameters }; + } + + /** + * Gets an AllDecorators object containing the decorators for the property. + * + * @param property The class property member. + */ + function getAllDecoratorsOfProperty(property: PropertyDeclaration): AllDecorators { + const decorators = property.decorators; + if (!decorators) { + return undefined; + + } + + return { decorators }; + } + + /** + * Transforms all of the decorators for a declaration into an array of expressions. + * + * @param node The declaration node. + * @param allDecorators The AllDecorators object for the node. + */ + function transformAllDecoratorsOfDeclaration(node: Declaration, allDecorators: AllDecorators) { + if (!allDecorators) { + return undefined; + } + + const decoratorExpressions: Expression[] = []; + addNodes(decoratorExpressions, map(allDecorators.decorators, transformDecorator)); + addNodes(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); + addTypeMetadata(node, decoratorExpressions); + return decoratorExpressions; + } + + /** + * Generates statements used to apply decorators to either the static or instance members + * of a class. + * + * @param node The class node. + * @param isStatic A value indicating whether to generate statements for static or + * instance members. + */ + function generateClassElementDecorationStatements(node: ClassDeclaration, isStatic: boolean) { + return map(generateClassElementDecorationExpressions(node, isStatic), expressionToStatement); + } + + /** + * Generates expressions used to apply decorators to either the static or instance members + * of a class. + * + * @param node The class node. + * @param isStatic A value indicating whether to generate expressions for static or + * instance members. + */ + function generateClassElementDecorationExpressions(node: ClassExpression | ClassDeclaration, isStatic: boolean) { + const members = getDecoratedClassElements(node, isStatic); + let expressions: Expression[]; + for (const member of members) { + const expression = generateClassElementDecorationExpression(node, member); + if (expression) { + if (!expressions) { + expressions = [expression]; + } + else { + expressions.push(expression); + } + } + } + return expressions; + } + + /** + * Generates an expression used to evaluate class element decorators at runtime. + * + * @param node The class node that contains the member. + * @param member The class member. + */ + function generateClassElementDecorationExpression(node: ClassExpression | ClassDeclaration, member: ClassElement) { + const allDecorators = getAllDecoratorsOfClassElement(node, member); + const decoratorExpressions = transformAllDecoratorsOfDeclaration(member, allDecorators); + if (!decoratorExpressions) { + return undefined; + } + + // Emit the call to __decorate. Given the following: + // + // class C { + // @dec method(@dec2 x) {} + // @dec get accessor() {} + // @dec prop; + // } + // + // The emit for a method is: + // + // __decorate([ + // dec, + // __param(0, dec2), + // __metadata("design:type", Function), + // __metadata("design:paramtypes", [Object]), + // __metadata("design:returntype", void 0) + // ], C.prototype, "method", undefined); + // + // The emit for an accessor is: + // + // __decorate([ + // dec + // ], C.prototype, "accessor", undefined); + // + // The emit for a property is: + // + // __decorate([ + // dec + // ], C.prototype, "prop"); + // + + const prefix = getClassMemberPrefix(node, member); + const memberName = getExpressionForPropertyName(member); + const descriptor = languageVersion > ScriptTarget.ES3 + ? member.kind === SyntaxKind.PropertyDeclaration + // We emit `void 0` here to indicate to `__decorate` that it can invoke `Object.defineProperty` directly, but that it + // should not invoke `Object.getOwnPropertyDescriptor`. + ? createVoidZero() + + // We emit `null` here to indicate to `__decorate` that it can invoke `Object.getOwnPropertyDescriptor` directly. + // We have this extra argument here so that we can inject an explicit property descriptor at a later date. + : createNull() + : undefined; + + return createDecorateHelper( + decoratorExpressions, + prefix, + memberName, + descriptor + ); + } + + /** + * Generates a __decorate helper call for a class constructor. + * + * @param node The class node. + */ + function generateConstructorDecorationStatement(node: ClassDeclaration, decoratedClassAlias: Identifier) { + const expression = generateConstructorDecorationExpression(node, decoratedClassAlias); + return expression ? createStatement(expression) : undefined; + } + + /** + * Generates a __decorate helper call for a class constructor. + * + * @param node The class node. + */ + function generateConstructorDecorationExpression(node: ClassExpression | ClassDeclaration, decoratedClassAlias: Identifier) { + const allDecorators = getAllDecoratorsOfConstructor(node); + const decoratorExpressions = transformAllDecoratorsOfDeclaration(node, allDecorators); + if (!decoratorExpressions) { + return undefined; + } + + // Emit the call to __decorate. Given the class: + // + // @dec + // class C { + // } + // + // The emit for the class is: + // + // C = __decorate([dec], C); + // + + const expression = createAssignment( + getDeclarationName(node), + createDecorateHelper( + decoratorExpressions, + getDeclarationName(node) + ) + ); + + return decoratedClassAlias + ? createAssignment(decoratedClassAlias, expression) + : expression; + } + + /** + * Transforms a decorator into an expression. + * + * @param decorator The decorator node. + */ + function transformDecorator(decorator: Decorator) { + return visitNode(decorator.expression, visitor, isExpression); + } + + /** + * Transforms the decorators of a parameter. + * + * @param decorators The decorators for the parameter at the provided offset. + * @param parameterOffset The offset of the parameter. + */ + function transformDecoratorsOfParameter(decorators: Decorator[], parameterOffset: number) { + let expressions: Expression[]; + if (decorators) { + expressions = []; + for (const decorator of decorators) { + expressions.push(createParamHelper(transformDecorator(decorator), parameterOffset)); + } + } + + return expressions; + } + + /** + * Adds optional type metadata for a declaration. + * + * @param node The declaration node. + * @param decoratorExpressions The destination array to which to add new decorator expressions. + */ + function addTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) { + if (compilerOptions.emitDecoratorMetadata) { + if (shouldAppendTypeMetadata(node)) { + decoratorExpressions.push(createMetadataHelper("design:type", serializeTypeOfNode(node), /*defer*/ true)); + } + if (shouldAppendParamTypesMetadata(node)) { + decoratorExpressions.push(createMetadataHelper("design:paramtypes", serializeParameterTypesOfNode(node), /*defer*/ true)); + } + if (shouldAppendReturnTypeMetadata(node)) { + decoratorExpressions.push(createMetadataHelper("design:returntype", serializeReturnTypeOfNode(node), /*defer*/ true)); + } + } + } + + /** + * Determines whether to emit the "design:type" metadata based on the node's kind. + * The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata + * compiler option is set. + * + * @param node The node to test. + */ + function shouldAppendTypeMetadata(node: Declaration): boolean { + const kind = node.kind; + return kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor + || kind === SyntaxKind.PropertyDeclaration; + } + + /** + * Determines whether to emit the "design:returntype" metadata based on the node's kind. + * The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata + * compiler option is set. + * + * @param node The node to test. + */ + function shouldAppendReturnTypeMetadata(node: Declaration): boolean { + return node.kind === SyntaxKind.MethodDeclaration; + } + + /** + * Determines whether to emit the "design:paramtypes" metadata based on the node's kind. + * The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata + * compiler option is set. + * + * @param node The node to test. + */ + function shouldAppendParamTypesMetadata(node: Declaration): boolean { + const kind = node.kind; + return kind === SyntaxKind.ClassDeclaration + || kind === SyntaxKind.ClassExpression + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor; + } + + /** + * Serializes the type of a node for use with decorator type metadata. + * + * @param node The node that should have its type serialized. + */ + function serializeTypeOfNode(node: Node): Expression { + switch (node.kind) { + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.GetAccessor: + return serializeTypeNode((node).type); + case SyntaxKind.SetAccessor: + return serializeTypeNode(getSetAccessorTypeAnnotationNode(node)); + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + case SyntaxKind.MethodDeclaration: + return createIdentifier("Function"); + default: + return createVoidZero(); + } + } + + /** + * Gets the most likely element type for a TypeNode. This is not an exhaustive test + * as it assumes a rest argument can only be an array type (either T[], or Array). + * + * @param node The type node. + */ + function getRestParameterElementType(node: TypeNode) { + if (node.kind === SyntaxKind.ArrayType) { + return (node).elementType; + } + else if (node.kind === SyntaxKind.TypeReference) { + return singleOrUndefined((node).typeArguments); + } + else { + return undefined; + } + } + + /** + * Serializes the types of the parameters of a node for use with decorator type metadata. + * + * @param node The node that should have its parameter types serialized. + */ + function serializeParameterTypesOfNode(node: Node): Expression { + const valueDeclaration = + isClassLike(node) + ? getFirstConstructorWithBody(node) + : isFunctionLike(node) && nodeIsPresent(node.body) + ? node + : undefined; + + const expressions: Expression[] = []; + if (valueDeclaration) { + for (const parameter of valueDeclaration.parameters) { + if (parameter.dotDotDotToken) { + expressions.push(serializeTypeNode(getRestParameterElementType(parameter.type))); + } + else { + expressions.push(serializeTypeOfNode(parameter)); + } + } + } + + return createArrayLiteral(expressions); + } + + /** + * Serializes the return type of a node for use with decorator type metadata. + * + * @param node The node that should have its return type serialized. + */ + function serializeReturnTypeOfNode(node: Node): Expression { + if (isFunctionLike(node)) { + return serializeTypeNode(node.type); + } + + return undefined; + } + + /** + * Serializes a type node for use with decorator type metadata. + * + * Types are serialized in the following fashion: + * - Void types point to "undefined" (e.g. "void 0") + * - Function and Constructor types point to the global "Function" constructor. + * - Interface types with a call or construct signature types point to the global + * "Function" constructor. + * - Array and Tuple types point to the global "Array" constructor. + * - Type predicates and booleans point to the global "Boolean" constructor. + * - String literal types and strings point to the global "String" constructor. + * - Enum and number types point to the global "Number" constructor. + * - Symbol types point to the global "Symbol" constructor. + * - Type references to classes (or class-like variables) point to the constructor for the class. + * - Anything else points to the global "Object" constructor. + * + * @param node The type node to serialize. + */ + function serializeTypeNode(node: TypeNode): Expression { + if (node === undefined) { + return createIdentifier("Object"); + } + + switch (node.kind) { + case SyntaxKind.VoidKeyword: + return createVoidZero(); + + case SyntaxKind.ParenthesizedType: + return serializeTypeNode((node).type); + + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + return createIdentifier("Function"); + + case SyntaxKind.ArrayType: + case SyntaxKind.TupleType: + return createIdentifier("Array"); + + case SyntaxKind.TypePredicate: + case SyntaxKind.BooleanKeyword: + return createIdentifier("Boolean") + + case SyntaxKind.StringKeyword: + case SyntaxKind.StringLiteral: + return createIdentifier("String"); + + case SyntaxKind.NumberKeyword: + return createIdentifier("Number"); + + case SyntaxKind.SymbolKeyword: + return languageVersion < ScriptTarget.ES6 + ? getGlobalSymbolNameWithFallback() + : createIdentifier("Symbol"); + + case SyntaxKind.TypeReference: + return serializeTypeReferenceNode(node); + + case SyntaxKind.TypeQuery: + case SyntaxKind.TypeLiteral: + case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: + case SyntaxKind.AnyKeyword: + break; + + default: + Debug.fail("Cannot serialize unexpected type node."); + break; + } + + return createIdentifier("Object"); + } + + /** + * Serializes a TypeReferenceNode to an appropriate JS constructor value for use with + * decorator type metadata. + * + * @param node The type reference node. + */ + function serializeTypeReferenceNode(node: TypeReferenceNode) { + // Clone the type name and parent it to a location outside of the current declaration. + const typeName = cloneEntityName(node.typeName, currentScope); + switch (resolver.getTypeReferenceSerializationKind(typeName)) { + case TypeReferenceSerializationKind.Unknown: + const serialized = serializeEntityNameAsExpression(typeName, /*useFallback*/ true); + const temp = createTempVariable(); + hoistVariableDeclaration(temp); + return createLogicalOr( + createLogicalAnd( + createStrictEquality( + createTypeOf( + createAssignment(temp, serialized) + ), + createLiteral("function") + ), + temp + ), + createIdentifier("Object") + ); + + case TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue: + return serializeEntityNameAsExpression(typeName, /*useFallback*/ false); + + case TypeReferenceSerializationKind.VoidType: + return createVoidZero(); + + case TypeReferenceSerializationKind.BooleanType: + return createIdentifier("Boolean"); + + case TypeReferenceSerializationKind.NumberLikeType: + return createIdentifier("Number"); + + case TypeReferenceSerializationKind.StringLikeType: + return createIdentifier("String"); + + case TypeReferenceSerializationKind.ArrayLikeType: + return createIdentifier("Array"); + + case TypeReferenceSerializationKind.ESSymbolType: + return languageVersion < ScriptTarget.ES6 + ? getGlobalSymbolNameWithFallback() + : createIdentifier("Symbol"); + + case TypeReferenceSerializationKind.TypeWithCallSignature: + return createIdentifier("Function"); + + case TypeReferenceSerializationKind.ObjectType: + default: + return createIdentifier("Object"); + } + } + + /** + * Serializes an entity name as an expression for decorator type metadata. + * + * @param node The entity name to serialize. + * @param useFallback A value indicating whether to use logical operators to test for the + * entity name at runtime. + */ + function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): Expression { + switch (node.kind) { + case SyntaxKind.Identifier: + if (useFallback) { + return createLogicalAnd( + createStrictInequality( + createTypeOf(node), + createLiteral("undefined") + ), + node + ); + } + + return node; + + case SyntaxKind.QualifiedName: + return serializeQualifiedNameAsExpression(node, useFallback); + } + } + + /** + * Serializes an qualified name as an expression for decorator type metadata. + * + * @param node The qualified name to serialize. + * @param useFallback A value indicating whether to use logical operators to test for the + * qualified name at runtime. + */ + function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): Expression { + let left: Expression + if (node.left.kind === SyntaxKind.Identifier) { + left = serializeEntityNameAsExpression(node.left, useFallback); + } + else if (useFallback) { + const temp = createTempVariable(); + hoistVariableDeclaration(temp); + left = createLogicalAnd( + createAssignment( + temp, + serializeEntityNameAsExpression(node.left, /*useFallback*/ true) + ), + temp + ); + } + else { + left = serializeEntityNameAsExpression(node.left, /*useFallback*/ false); + } + + return createPropertyAccess(left, node.right); + } + + /** + * Gets an expression that points to the global "Symbol" constructor at runtime if it is + * available. + */ + function getGlobalSymbolNameWithFallback(): Expression { + return createConditional( + createStrictEquality( + createTypeOf(createIdentifier("Symbol")), + createLiteral("function") + ), + createIdentifier("Symbol"), + createIdentifier("Object") + ); + } + + /** + * Gets an expression that represents a property name. For a computed property, a + * name is generated for the node. + * + * @param member The member whose name should be converted into an expression. + */ + function getExpressionForPropertyName(member: ClassElement | EnumMember): Expression { + const name = member.name; + if (isComputedPropertyName(name)) { + return getGeneratedNameForNode(name); + } + else if (isIdentifier(name)) { + return createLiteral(name.text); + } + else { + return getSynthesizedNode(name); + } + } + + /** + * Visits the property name of a class element, for use when emitting property + * initializers. For a computed property on a node with decorators, a temporary + * value is stored for later use. + * + * @param member The member whose name should be visited. + */ + function visitPropertyNameOfClassElement(member: ClassElement): PropertyName { + const name = member.name; + if (isComputedPropertyName(name)) { + let expression = visitNode(name.expression, visitor, isExpression); + if (member.decorators) { + const generatedName = getGeneratedNameForNode(name); + hoistVariableDeclaration(generatedName); + expression = createAssignment(generatedName, expression); + } + + return setOriginalNode( + createComputedPropertyName(expression, /*location*/ name), + name + ); + } + else { + return setOriginalNode( + cloneNode(name), + name + ); + } + } + + /** + * Transforms a HeritageClause with TypeScript syntax. + * + * This function will only be called when one of the following conditions are met: + * - The node is a non-`extends` heritage clause that should be elided. + * - The node is an `extends` heritage clause that should be visited, but only allow a single type. + * + * @param node The HeritageClause to transform. + */ + function visitHeritageClause(node: HeritageClause): HeritageClause { + if (node.token === SyntaxKind.ExtendsKeyword) { + const types = visitNodes(node.types, visitor, isExpressionWithTypeArguments, 0, 1); + return createHeritageClause( + SyntaxKind.ExtendsKeyword, + types, + node + ); + } + + return undefined; + } + + /** + * Transforms an ExpressionWithTypeArguments with TypeScript syntax. + * + * This function will only be called when one of the following conditions are met: + * - The node contains type arguments that should be elided. + * + * @param node The ExpressionWithTypeArguments to transform. + */ + function visitExpressionWithTypeArguments(node: ExpressionWithTypeArguments): ExpressionWithTypeArguments { + const expression = visitNode(node.expression, visitor, isLeftHandSideExpression); + return createExpressionWithTypeArguments( + expression, + node + ); + } + + /** + * Visits a method declaration of a class. + * + * This function will be called when one of the following conditions are met: + * - The node is an overload + * - The node is marked as abstract + * - The node is marked as async + * - The node has both a decorator and a computed property name + * + * @param node The method node. + */ + function visitMethodDeclaration(node: MethodDeclaration) { + if (shouldElideFunctionLikeDeclaration(node)) { + return undefined; + } + + return createMethod( + visitNodes(node.modifiers, visitor, isModifier), + visitPropertyNameOfClassElement(node), + visitNodes(node.parameters, visitor, isParameter), + transformFunctionBody(node), + node + ); + } + + /** + * Visits a get accessor declaration of a class. + * + * This function will be called when one of the following conditions are met: + * - The node is marked as abstract + * - The node has both a decorator and a computed property name + * + * @param node The get accessor node. + */ + function visitGetAccessor(node: GetAccessorDeclaration) { + if (shouldElideFunctionLikeDeclaration(node)) { + return undefined; + } + + return createGetAccessor( + visitNodes(node.modifiers, visitor, isModifier), + visitPropertyNameOfClassElement(node), + visitEachChild(node.body, visitor, context), + node + ); + } + + /** + * Visits a set accessor declaration of a class. + * + * This function will be called when one of the following conditions are met: + * - The node is marked as abstract + * - The node has both a decorator and a computed property name + * + * @param node The set accessor node. + */ + function visitSetAccessor(node: SetAccessorDeclaration) { + if (shouldElideFunctionLikeDeclaration(node)) { + return undefined; + } + + return createSetAccessor( + visitNodes(node.modifiers, visitor, isModifier), + visitPropertyNameOfClassElement(node), + visitNode(firstOrUndefined(node.parameters), visitor, isParameter), + visitEachChild(node.body, visitor, context), + node + ); + } + + /** + * Visits a function declaration. + * + * This function will be called when one of the following conditions are met: + * - The node is an overload + * - The node is marked async + * - The node is exported from a TypeScript namespace + * + * @param node The function node. + */ + function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMore { + if (shouldElideFunctionLikeDeclaration(node)) { + return undefined; + } + + const func = createFunctionDeclaration( + visitNodes(node.modifiers, visitor, isModifier), + node.asteriskToken, + node.name, + visitNodes(node.parameters, visitor, isParameter), + transformFunctionBody(node), + node + ); + + if (isNamespaceExport(node)) { + return createNodeArrayNode([ + func, + createNamespaceExport(getSynthesizedNode(node.name), getSynthesizedNode(node.name)) + ]); + } + + return func; + } + + /** + * Visits a function expression node. + * + * This function will be called when one of the following conditions are met: + * - The node is marked async + * + * @param node The function expression node. + */ + function visitFunctionExpression(node: FunctionExpression) { + return createFunctionExpression( + node.asteriskToken, + node.name, + visitNodes(node.parameters, visitor, isParameter), + transformFunctionBody(node), + node + ); + } + + /** + * Determines whether a function-like declaration should be elided. A declaration should + * be elided if it is an overload, is abstract, or is an ambient declaration. + * + * @param node The declaration node. + */ + function shouldElideFunctionLikeDeclaration(node: FunctionLikeDeclaration) { + return node.body === undefined + || node.flags & (NodeFlags.Abstract | NodeFlags.Ambient); + } + + /** + * @remarks + * This function will be called when one of the following conditions are met: + * - The node is marked async + */ + function visitArrowFunction(node: ArrowFunction) { + return createArrowFunction( + visitNodes(node.parameters, visitor, isParameter), + transformConciseBody(node), + node + ); + } + + function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody { + if (isAsyncFunctionLike(node)) { + return transformAsyncFunctionBody(node); + } + + return transformFunctionBodyWorker(node.body); + } + + function transformFunctionBodyWorker(body: Block) { + const savedCurrentScope = currentScope; + currentScope = body; + startLexicalEnvironment(); + const visited = visitEachChild(body, visitor, context); + const declarations = endLexicalEnvironment(); + currentScope = savedCurrentScope; + return mergeFunctionBodyLexicalEnvironment(visited, declarations, visited !== body); + } + + function transformConciseBody(node: ArrowFunction): ConciseBody { + if (isAsyncFunctionLike(node)) { + return transformAsyncFunctionBody(node); + } + + return transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ false); + } + + function transformConciseBodyWorker(body: Block | Expression, forceBlockFunctionBody: boolean) { + if (isBlock(body)) { + return transformFunctionBodyWorker(body); + } + else { + startLexicalEnvironment(); + const visited: Expression | Block = visitNode(body, visitor, isConciseBody); + const declarations = endLexicalEnvironment(); + const merged = mergeConciseBodyLexicalEnvironment(visited, declarations, visited !== body); + if (forceBlockFunctionBody && !isBlock(merged)) { + return createBlock([ + createReturn(merged) + ]); + } + else { + return merged; + } + } + } + + function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody | FunctionBody { + const promiseConstructor = getEntityNameFromTypeNode(node.type); + const isArrowFunction = node.kind === SyntaxKind.ArrowFunction; + const hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0; + + // An async function is emit as an outer function that calls an inner + // generator function. To preserve lexical bindings, we pass the current + // `this` and `arguments` objects to `__awaiter`. The generator function + // passed to `__awaiter` is executed inside of the callback to the + // promise constructor. + // + // The emit for an async arrow without a lexical `arguments` binding might be: + // + // // input + // let a = async (b) => { await b; } + // + // // output + // let a = (b) => __awaiter(this, void 0, void 0, function* () { + // yield b; + // }); + // + // The emit for an async arrow with a lexical `arguments` binding might be: + // + // // input + // let a = async (b) => { await arguments[0]; } + // + // // output + // let a = (b) => __awaiter(this, arguments, void 0, function* (arguments) { + // yield arguments[0]; + // }); + // + // The emit for an async function expression without a lexical `arguments` binding + // might be: + // + // // input + // let a = async function (b) { + // await b; + // } + // + // // output + // let a = function (b) { + // return __awaiter(this, void 0, void 0, function* () { + // yield b; + // }); + // } + // + // The emit for an async function expression with a lexical `arguments` binding + // might be: + // + // // input + // let a = async function (b) { + // await arguments[0]; + // } + // + // // output + // let a = function (b) { + // return __awaiter(this, arguments, void 0, function* (_arguments) { + // yield _arguments[0]; + // }); + // } + // + // The emit for an async function expression with a lexical `arguments` binding + // and a return type annotation might be: + // + // // input + // let a = async function (b): MyPromise { + // await arguments[0]; + // } + // + // // output + // let a = function (b) { + // return __awaiter(this, arguments, MyPromise, function* (_arguments) { + // yield _arguments[0]; + // }); + // } + // + + if (!isArrowFunction) { + const statements: Statement[] = []; + + addNode(statements, + createReturn( + createAwaiterHelper( + hasLexicalArguments, + promiseConstructor, + transformFunctionBodyWorker(node.body) + ) + ) + ); + + const block = createBlock(statements); + + // Minor optimization, emit `_super` helper to capture `super` access in an arrow. + // This step isn't needed if we eventually transform this to ES5. + if (languageVersion >= ScriptTarget.ES6) { + if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) { + enableExpressionSubstitutionForAsyncMethodsWithSuper(); + setNodeEmitFlags(block, NodeEmitFlags.EmitAdvancedSuperHelper); + } + else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) { + enableExpressionSubstitutionForAsyncMethodsWithSuper(); + setNodeEmitFlags(block, NodeEmitFlags.EmitSuperHelper); + } + } + + return block; + } + else { + return createAwaiterHelper( + hasLexicalArguments, + promiseConstructor, + transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ true) + ); + } + } + + function enableExpressionSubstitutionForAsyncMethodsWithSuper() { + if (!hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) { + hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = true; + + // We need to enable substitutions for call, property access, and element access + // if we need to rewrite super calls. + context.enableExpressionSubstitution(SyntaxKind.CallExpression); + context.enableExpressionSubstitution(SyntaxKind.PropertyAccessExpression); + context.enableExpressionSubstitution(SyntaxKind.ElementAccessExpression); + + // We need to be notified when entering and exiting declarations that bind super. + context.enableEmitNotification(SyntaxKind.ClassDeclaration); + context.enableEmitNotification(SyntaxKind.MethodDeclaration); + context.enableEmitNotification(SyntaxKind.GetAccessor); + context.enableEmitNotification(SyntaxKind.SetAccessor); + context.enableEmitNotification(SyntaxKind.Constructor); + } + } + + /** + * Visits a parameter declaration node. + * + * This function will be called when one of the following conditions are met: + * - The node has an accessibility modifier. + * + * @param node The parameter declaration node. + */ + function visitParameter(node: ParameterDeclaration) { + Debug.assert(!node.dotDotDotToken); + return createParameter( + visitNode(node.name, visitor, isBindingName), + visitNode(node.initializer, visitor, isExpression) + ); + } + + /** + * Visits a variable statement in a namespace. + * + * 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)); + + 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 getInitializedVariables(node: VariableDeclarationList) { + return filter(node.declarations, isInitializedVariable); + } + + function isInitializedVariable(node: VariableDeclaration) { + return node.initializer !== undefined; + } + + function transformInitializedVariable(node: VariableDeclaration): Expression { + const name = node.name; + if (isBindingPattern(name)) { + return flattenVariableDestructuringToExpression( + node, + hoistVariableDeclaration, + getNamespaceMemberName, + visitor + ); + } + else { + return createAssignment( + getNamespaceMemberName(name), + visitNode(node.initializer, visitor, isExpression) + ); + } + } + + /** + * Visits an enum declaration. + * + * This function will be called any time a TypeScript enum is encountered. + * + * @param node The enum declaration node. + */ + function visitEnumDeclaration(node: EnumDeclaration) { + if (shouldElideEnumDeclaration(node)) { + return undefined; + } + + const savedCurrentNamespaceLocalName = currentNamespaceLocalName; + const modifiers = visitNodes(node.modifiers, visitor, isModifier); + const statements: Statement[] = []; + + let location: TextRange = node; + if (!isNamespaceExport(node)) { + addNode(statements, + createVariableStatement( + modifiers, + createVariableDeclarationList([ + createVariableDeclaration(node.name) + ]), + location + ) + ); + location = undefined; + } + + const namespaceMemberName = getNamespaceMemberName(node.name); + currentNamespaceLocalName = getGeneratedNameForNode(node); + addNode(statements, + createStatement( + createCall( + createParen( + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + [createParameter(currentNamespaceLocalName)], + transformEnumBody(node) + ) + ), + [createLogicalOr( + namespaceMemberName, + createAssignment( + namespaceMemberName, + createObjectLiteral() + ) + )] + ), + location + ) + ); + + if (isNamespaceExport(node)) { + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration(node.name, namespaceMemberName) + ]), + location + ) + ); + } + + currentNamespaceLocalName = savedCurrentNamespaceLocalName; + return createNodeArrayNode(statements); + } + + /** + * Transforms the body of an enum declaration. + * + * @param node The enum declaration node. + */ + function transformEnumBody(node: EnumDeclaration): Block { + const statements: Statement[] = []; + startLexicalEnvironment(); + addNodes(statements, map(node.members, transformEnumMember)); + addNodes(statements, endLexicalEnvironment()); + return createBlock(statements); + } + + /** + * Transforms an enum member into a statement. + * + * @param member The enum member node. + */ + function transformEnumMember(member: EnumMember) { + const name = getExpressionForPropertyName(member); + return createStatement( + createAssignment( + createElementAccess( + currentNamespaceLocalName, + createAssignment( + createElementAccess( + currentNamespaceLocalName, + name + ), + transformEnumMemberDeclarationValue(member) + ) + ), + name + ), + member + ); + } + + /** + * Transforms the value of an enum member. + * + * @param member The enum member node. + */ + function transformEnumMemberDeclarationValue(member: EnumMember): Expression { + let value = resolver.getConstantValue(member); + if (value !== undefined) { + return createLiteral(value); + } + else if (member.initializer) { + return visitNode(member.initializer, visitor, isExpression); + } + else { + return createVoidZero(); + } + } + + /** + * Determines whether to elide an enum declaration. + * + * @param node The enum declaration node. + */ + function shouldElideEnumDeclaration(node: EnumDeclaration) { + return isConst(node) + && !compilerOptions.preserveConstEnums + && !compilerOptions.isolatedModules; + } + + /** + * Visits an await expression. + * + * This function will be called any time a TypeScript await expression is encountered. + * + * @param node The await expression node. + */ + function visitAwaitExpression(node: AwaitExpression): Expression { + const expression = createYield( + visitNode(node.expression, visitor, isExpression), + node + ); + + return isRightmostExpression ? expression : createParen(expression); + } + + /** + * Visits a parenthesized expression that contains either a type assertion or an `as` + * expression. + * + * @param node The parenthesized expression node. + */ + function visitParenthesizedExpression(node: ParenthesizedExpression): Expression { + // Make sure we consider all nested cast expressions, e.g.: + // (-A).x; + const expression = visitNode(node.expression, visitor, isExpression); + if (currentParent.kind !== SyntaxKind.ArrowFunction) { + // We have an expression of the form: (SubExpr) + // Emitting this as (SubExpr) is really not desirable. We would like to emit the subexpr as is. + // Omitting the parentheses, however, could cause change in the semantics of the generated + // code if the casted expression has a lower precedence than the rest of the expression, e.g.: + // (new A).foo should be emitted as (new A).foo and not new A.foo + // (typeof A).toString() should be emitted as (typeof A).toString() and not typeof A.toString() + // new (A()) should be emitted as new (A()) and not new A() + // (function foo() { })() should be emitted as an IIF (function foo(){})() and not declaration function foo(){} () + if (expression.kind !== SyntaxKind.PrefixUnaryExpression && + expression.kind !== SyntaxKind.VoidExpression && + expression.kind !== SyntaxKind.TypeOfExpression && + expression.kind !== SyntaxKind.DeleteExpression && + expression.kind !== SyntaxKind.PostfixUnaryExpression && + expression.kind !== SyntaxKind.NewExpression && + !(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 createParen(expression, node); + } + + /** + * Visits a module declaration node. + * + * This function will be called any time a TypeScript namespace (ModuleDeclaration) is encountered. + * + * @param node The module declaration node. + */ + function visitModuleDeclaration(node: ModuleDeclaration) { + if (shouldElideModuleDeclaration(node)) { + return undefined; + } + + Debug.assert(isIdentifier(node.name)); + + const savedCurrentNamespaceLocalName = currentNamespaceLocalName; + const modifiers = visitNodes(node.modifiers, visitor, isModifier); + const statements: Statement[] = []; + + let location = node; + if (!isModuleMergedWithClass(node)) { + // var x; + statements.push( + createVariableStatement( + modifiers, + createVariableDeclarationList([ + createVariableDeclaration(node.name) + ]), + location + ) + ); + location = undefined; + } + + let moduleParam: Expression = createLogicalOr( + getNamespaceMemberName(node.name), + createAssignment( + getNamespaceMemberName(node.name), + createObjectLiteral([]) + ) + ); + + if (isNamespaceExport(node)) { + moduleParam = createAssignment(cloneNode(node.name), moduleParam); + } + + currentNamespaceLocalName = getGeneratedNameForNode(node); + currentNamespace = node; + + // (function (x_1) { + // x_1.y = ...; + // })(x || (x = {})); + statements.push( + setOriginalNode( + createStatement( + createCall( + createParen( + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + [createParameter(currentNamespaceLocalName)], + transformModuleBody(node) + ) + ), + [moduleParam] + ) + ), + node + ) + ); + + currentNamespaceLocalName = savedCurrentNamespaceLocalName; + return createNodeArrayNode(statements); + } + + /** + * Transforms the body of a module declaration. + * + * @param node The module declaration node. + */ + function transformModuleBody(node: ModuleDeclaration): Block { + const statements: Statement[] = []; + startLexicalEnvironment(); + const body = node.body; + if (body.kind === SyntaxKind.ModuleBlock) { + addNodes(statements, visitNodes((body).statements, namespaceElementVisitor, isStatement)); + } + else { + addNode(statements, visitModuleDeclaration(body)); + } + + addNodes(statements, endLexicalEnvironment()); + return createBlock(statements); + } + + /** + * Determines whether to elide a module declaration. + * + * @param node The module declaration node. + */ + function shouldElideModuleDeclaration(node: ModuleDeclaration) { + return !isInstantiatedModule(node, compilerOptions.preserveConstEnums || compilerOptions.isolatedModules); + } + + /** + * Determines whether a module declaration has a name that merges with a class declaration. + * + * @param node The module declaration node. + */ + function isModuleMergedWithClass(node: ModuleDeclaration) { + return !!(resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.LexicalModuleMergesWithClass); + } + + /** + * Visits an import equals declaration. + * + * @param node The import equals declaration node. + */ + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMore { + Debug.assert(!isExternalModuleImportEqualsDeclaration(node)); + if (shouldElideImportEqualsDeclaration(node)) { + return undefined; + } + + const moduleReference = createExpressionFromEntityName(node.moduleReference); + if (isNamedExternalModuleExport(node) || !isNamespaceExport(node)) { + // export var ${name} = ${moduleReference}; + // var ${name} = ${moduleReference}; + return setOriginalNode( + createVariableStatement( + visitNodes(node.modifiers, visitor, isModifier), + createVariableDeclarationList([ + createVariableDeclaration(node.name, moduleReference) + ]), + node + ), + node + ); + } + else { + // exports.${name} = ${moduleReference}; + return setOriginalNode( + createNamespaceExport( + getSynthesizedNode(node.name), + moduleReference, + node + ), + node + ); + } + } + + /** + * Determines whether to elide an import equals declaration. + * + * @param node The import equals declaration node. + */ + function shouldElideImportEqualsDeclaration(node: ImportEqualsDeclaration) { + // preserve old compiler's behavior: emit 'var' for import declaration (even if we do not consider them referenced) when + // - current file is not external module + // - import declaration is top level and target is value imported by entity name + return !resolver.isReferencedAliasDeclaration(node) + && (isExternalModule(currentSourceFile) || !resolver.isTopLevelValueImportEqualsWithEntityName(node)); + } + + /** + * Gets a value indicating whether the node is exported from an external module. + * + * @param node The node to test. + */ + function isExternalModuleExport(node: Node) { + return currentNamespace === undefined + && (node.flags & NodeFlags.Export) !== 0; + } + + /** + * Gets a value indicating whether the node is a named export from an external module. + * + * @param node The node to test. + */ + function isNamedExternalModuleExport(node: Node) { + return isExternalModuleExport(node) + && (node.flags & NodeFlags.Default) === 0; + } + + /** + * Gets a value indicating whether the node is the default export of an external module. + * + * @param node The node to test. + */ + function isDefaultExternalModuleExport(node: Node) { + return isExternalModuleExport(node) + && (node.flags & NodeFlags.Default) !== 0; + } + + /** + * Gets a value indicating whether the node is exported from a namespace. + * + * @param node The node to test. + */ + function isNamespaceExport(node: Node) { + return currentNamespace !== undefined + && (node.flags & NodeFlags.Export) !== 0; + } + + /** + * Creates a statement for the provided expression. This is used in calls to `map`. + */ + function expressionToStatement(expression: Expression) { + return createStatement(expression, /*location*/ undefined); + } + + function createExportStatement(node: ClassExpression | ClassDeclaration | FunctionDeclaration): Statement { + const name = getDeclarationName(node); + if (currentNamespace) { + return createNamespaceExport(name, name); + } + else if (node.flags & NodeFlags.Default) { + return createExportDefault(name); + } + else { + return createModuleExport(name); + } + } + + function createNamespaceExport(exportName: Identifier, exportValue: Expression, location?: TextRange) { + return createStatement( + createAssignment( + getNamespaceMemberName(exportName), + exportValue + ), + location + ); + } + + function createModuleExport(exportName: Identifier) { + return createExportDeclaration( + createNamedExports([ + createExportSpecifier(exportName) + ]) + ); + } + + function getNamespaceMemberName(name: Identifier): Expression { + name = getSynthesizedNode(name); + return currentNamespaceLocalName + ? createPropertyAccess(currentNamespaceLocalName, name) + : name + } + + function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) { + return node.name ? getSynthesizedNode(node.name) : getGeneratedNameForNode(node); + } + + function getClassPrototype(node: ClassExpression | ClassDeclaration) { + return createPropertyAccess(getDeclarationName(node), "prototype"); + } + + function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) { + return member.flags & NodeFlags.Static + ? getDeclarationName(node) + : getClassPrototype(node); + } + + function substituteExpression(node: Expression): Expression { + node = previousExpressionSubstitution ? previousExpressionSubstitution(node) : node; + switch (node.kind) { + case SyntaxKind.Identifier: + return substituteExpressionIdentifier(node); + } + + if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) { + switch (node.kind) { + case SyntaxKind.CallExpression: + return substituteCallExpression(node); + case SyntaxKind.PropertyAccessExpression: + return substitutePropertyAccessExpression(node); + case SyntaxKind.ElementAccessExpression: + return substituteElementAccessExpression(node); + } + } + + return node; + } + + function substituteExpressionIdentifier(node: Identifier) { + if (!nodeIsSynthesized(node) && resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) { + // 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 original = getOriginalNode(node); + const declaration = resolver.getReferencedValueDeclaration(isIdentifier(original) ? original : node); + if (declaration) { + const classAlias = currentDecoratedClassAliases[getNodeId(declaration)]; + if (classAlias) { + return cloneNode(classAlias); + } + } + } + + return node; + } + + function substituteCallExpression(node: CallExpression) { + const expression = node.expression; + if (isSuperPropertyOrElementAccess(expression)) { + const flags = getSuperContainerAsyncMethodFlags(); + if (flags) { + const argumentExpression = isPropertyAccessExpression(expression) + ? substitutePropertyAccessExpression(expression) + : substituteElementAccessExpression(expression); + return createCall( + createPropertyAccess(argumentExpression, "call"), + [ + createThis(), + ...node.arguments + ] + ); + } + } return node; } + + function substitutePropertyAccessExpression(node: PropertyAccessExpression) { + if (node.expression.kind === SyntaxKind.SuperKeyword) { + const flags = getSuperContainerAsyncMethodFlags(); + if (flags) { + return createSuperAccessInAsyncMethod( + createLiteral(node.name.text), + flags, + node + ); + } + } + + return node; + } + + function substituteElementAccessExpression(node: ElementAccessExpression) { + if (node.expression.kind === SyntaxKind.SuperKeyword) { + const flags = getSuperContainerAsyncMethodFlags(); + if (flags) { + return createSuperAccessInAsyncMethod( + node.argumentExpression, + flags, + node + ); + } + } + + return node; + } + + + function createSuperAccessInAsyncMethod(argumentExpression: Expression, flags: NodeCheckFlags, location: TextRange): LeftHandSideExpression { + if (flags & NodeCheckFlags.AsyncMethodWithSuperBinding) { + return createPropertyAccess( + createCall( + createIdentifier("_super"), + [argumentExpression] + ), + "value", + location + ); + } + else { + return createCall( + createIdentifier("_super"), + [argumentExpression], + location + ); + } + } + + function getSuperContainerAsyncMethodFlags() { + const container = lastOrUndefined(superContainerStack); + return container !== undefined + && resolver.getNodeCheckFlags(getOriginalNode(container)) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding); + } + + function onBeforeEmitNode(node: Node): void { + const kind = node.kind; + if (kind === SyntaxKind.ClassDeclaration && node.decorators) { + currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)]; + } + + if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper + && (kind === SyntaxKind.ClassDeclaration + || kind === SyntaxKind.Constructor + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor)) { + + if (!superContainerStack) { + superContainerStack = []; + } + + superContainerStack.push(node); + } + } + + function onAfterEmitNode(node: Node): void { + const kind = node.kind; + if (kind === SyntaxKind.ClassDeclaration && node.decorators) { + currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined; + } + + if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper + && (kind === SyntaxKind.ClassDeclaration + || kind === SyntaxKind.Constructor + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor)) { + + if (superContainerStack) { + superContainerStack.pop(); + } + } + } + + function isRightmost(parentNode: Node, node: Node, wasRightmost: boolean) { + switch (parentNode.kind) { + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.BindingElement: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertyAssignment: + case SyntaxKind.ShorthandPropertyAssignment: + case SyntaxKind.ExpressionStatement: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ThrowStatement: + case SyntaxKind.ReturnStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.SwitchStatement: + case SyntaxKind.CaseClause: + case SyntaxKind.WithStatement: + case SyntaxKind.Decorator: + case SyntaxKind.ExpressionWithTypeArguments: + case SyntaxKind.SpreadElementExpression: + case SyntaxKind.AsExpression: + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.EnumMember: + case SyntaxKind.JsxAttribute: + case SyntaxKind.JsxSpreadAttribute: + case SyntaxKind.JsxExpression: + return true; + case SyntaxKind.TemplateSpan: + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + return node !== (parentNode).expression; + case SyntaxKind.BinaryExpression: + return wasRightmost && node === (parentNode).right; + case SyntaxKind.ConditionalExpression: + return wasRightmost && node === (parentNode).whenTrue; + default: + return wasRightmost; + } + } } } \ No newline at end of file diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6cd3a66678881..2ba3e4fa11859 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1301,7 +1301,7 @@ namespace ts { export interface EnumMember extends Declaration { // This does include ComputedPropertyName, but the parser will give an error // if it parses a ComputedPropertyName in an EnumMember - name: DeclarationName; + name: PropertyName; initializer?: Expression; } @@ -2792,12 +2792,15 @@ namespace ts { /* @internal */ export const enum NodeEmitFlags { - EmitHelpers = 1 << 0, // Any emit helpers should be written to this node. - EmitExportStar = 1 << 1, // The export * helper should be written to this node. - UMDDefine = 1 << 2, // This node should be replaced with the UMD define helper. - NoLexicalEnvironment = 1 << 3, // A new LexicalEnvironment should *not* be introduced when emitting this node. - SingleLine = 1 << 4, // The contents of this node should be emit on a single line. - MultiLine = 1 << 5, // The contents of this node should be emit on multiple lines. + EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node. + EmitExportStar = 1 << 1, // The export * helper should be written to this node. + EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods. + EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods. + UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. + NoLexicalEnvironment = 1 << 5, // A new LexicalEnvironment should *not* be introduced when emitting this node. + SingleLine = 1 << 6, // The contents of this node should be emit on a single line. + MultiLine = 1 << 7, // The contents of this node should be emit on multiple lines. + AdviseOnEmitNode = 1 << 8, // The node printer should invoke the onBeforeEmitNode and onAfterEmitNode callbacks when printing this node. } /** Additional context provided to `visitEachChild` */ @@ -2821,8 +2824,14 @@ namespace ts { getGeneratedNameForNode(node: Node): Identifier; nodeHasGeneratedName(node: Node): boolean; makeUniqueName(baseName: string): Identifier; + enableExpressionSubstitution(kind: SyntaxKind): void; + isExpressionSubstitutionEnabled(node: Node): boolean; identifierSubstitution?: (node: Identifier) => Identifier; expressionSubstitution?: (node: Expression) => Expression; + enableEmitNotification(kind: SyntaxKind): void; + isEmitNotificationEnabled(node: Node): boolean; + onBeforeEmitNode?: (node: Node) => void; + onAfterEmitNode?: (node: Node) => void; } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 15e288ef60133..563921e7e9b1f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1018,6 +1018,20 @@ namespace ts { && nodeCanBeDecorated(node); } + export function nodeOrChildIsDecorated(node: Node): boolean { + return nodeIsDecorated(node) || childIsDecorated(node); + } + + export function childIsDecorated(node: Node): boolean { + switch (node.kind) { + case SyntaxKind.ClassDeclaration: + return forEach((node).members, nodeOrChildIsDecorated); + case SyntaxKind.MethodDeclaration: + case SyntaxKind.SetAccessor: + return forEach((node).parameters, nodeIsDecorated); + } + } + export function isPartOfExpression(node: Node): boolean { switch (node.kind) { case SyntaxKind.SuperKeyword: @@ -2252,7 +2266,14 @@ namespace ts { return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type; } - export function getAllAccessorDeclarations(declarations: NodeArray, accessor: AccessorDeclaration) { + export interface AllAccessorDeclarations { + firstAccessor: AccessorDeclaration; + secondAccessor: AccessorDeclaration; + getAccessor: AccessorDeclaration; + setAccessor: AccessorDeclaration; + } + + export function getAllAccessorDeclarations(declarations: NodeArray, accessor: AccessorDeclaration): AllAccessorDeclarations { let firstAccessor: AccessorDeclaration; let secondAccessor: AccessorDeclaration; let getAccessor: AccessorDeclaration; @@ -2816,7 +2837,6 @@ namespace ts { || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor - || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.IndexSignature; } @@ -2849,6 +2869,10 @@ namespace ts { return node.kind === SyntaxKind.BinaryExpression; } + export function isConditionalExpression(node: Node): node is ConditionalExpression { + return node.kind === SyntaxKind.ConditionalExpression; + } + export function isShortHandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { return node.kind === SyntaxKind.ShorthandPropertyAssignment; } @@ -3241,8 +3265,12 @@ namespace ts { return node.kind === SyntaxKind.NodeArrayNode; } - export function isModifiersArray(array: NodeArray): array is ModifiersArray { - return array.arrayKind === ArrayKind.ModifiersArray; + export function isNodeArray(array: T[]): array is NodeArray { + return (>array).arrayKind === ArrayKind.ModifiersArray; + } + + export function isModifiersArray(array: Modifier[]): array is ModifiersArray { + return (array).arrayKind === ArrayKind.ModifiersArray; } } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 6e63524a23c99..ee68fce3a6a6f 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -2,6 +2,8 @@ /* @internal */ namespace ts { + export type OneOrMore = T | NodeArrayNode; + /** * Describes an edge of a Node, used when traversing a syntax tree. */ @@ -472,7 +474,7 @@ namespace ts { * @param lift A callback to execute to lift a NodeArrayNode into a valid Node. * @param optional A value indicating whether the Node is optional. */ - export function visitNode(node: T, visitor: (node: Node) => Node, test?: (node: Node) => boolean, lift?: (node: NodeArray) => T, optional?: boolean): T { + export function visitNode(node: T, visitor: (node: Node) => Node, test?: (node: Node) => boolean, optional?: boolean, lift?: (node: NodeArray) => T): T { if (node === undefined) { return undefined; } @@ -490,6 +492,7 @@ namespace ts { Debug.assert(test === undefined || test(visited), "Wrong node type after visit."); aggregateTransformFlags(visited); + visited.original = node; return visited; } @@ -500,54 +503,55 @@ namespace ts { * @param visitor The callback used to visit a Node. * @param test A node test to execute for each node. */ - export function visitNodes>(nodes: TArray, visitor: (node: Node) => Node, test?: (node: Node) => boolean): TArray { + export function visitNodes>(nodes: TArray, visitor: (node: Node) => Node, test?: (node: Node) => boolean, start?: number, count?: number): TArray; + export function visitNodes(nodes: T[], visitor: (node: T) => U): NodeArray; + export function visitNodes(nodes: T[], visitor: (node: Node) => Node, test?: (node: Node) => boolean, start?: number, count?: number): NodeArray; + export function visitNodes>(nodes: TArray, visitor: (node: Node) => Node, test?: (node: Node) => boolean, start?: number, count?: number): TArray { if (nodes === undefined) { return undefined; } - let updated: NodeArray | ModifiersArray; - for (let i = 0, len = nodes.length; i < len; i++) { - const node = nodes[i]; + const len = nodes.length; + start = start !== undefined ? Math.max(start, 0) : 0; + count = count !== undefined ? Math.min(count, len - start) : len - start; + + let updated: T[]; + if (start > 0 || count < len) { + updated = []; + } + + for (let i = 0; i < count; i++) { + const node = nodes[i + start]; if (node === undefined) { continue; } - const visited = visitor(node); + const visited = >visitor(node); if (updated !== undefined || visited === undefined || visited !== node) { if (updated === undefined) { - updated = isModifiersArray(nodes) - ? createModifiersArray(nodes.slice(0, i), nodes.pos, nodes.end) - : createNodeArray(nodes.slice(0, i), nodes.pos, nodes.end); + updated = nodes.slice(0, i); } if (visited === undefined) { continue; } - if (isNodeArrayNode(visited)) { - spreadNodeArrayNode(visited, updated, test); + if (visited !== node) { + aggregateTransformFlags(visited); + visited.original = node; } - else if (visited !== undefined) { - Debug.assert(test === undefined || test(visited), "Wrong node type after visit."); - if (visited !== node) { - aggregateTransformFlags(visited); - } - updated.push(visited); - } + addNode(updated, visited, test); } } - if (updated && isModifiersArray(updated)) { - let flags: NodeFlags = 0; - for (const node of updated) { - flags |= modifierToFlag(node.kind); - } - - updated.flags = flags; + if (updated) { + return (isModifiersArray(nodes) + ? createModifiersArray(updated, /*location*/ nodes) + : createNodeArray(updated, /*location*/ nodes)); } - return updated || nodes; + return nodes; } /** @@ -606,6 +610,7 @@ namespace ts { if (updated !== node) { aggregateTransformFlags(updated); + updated.original = node; } return updated; @@ -621,24 +626,40 @@ namespace ts { function visitEdge(edge: NodeEdge, value: Node | NodeArray, visitor: (node: Node) => Node) { return isArray(value) ? visitNodes(>value, visitor, edge.test) - : visitNode(value, visitor, edge.test, edge.lift, edge.optional); + : visitNode(value, visitor, edge.test, edge.optional, edge.lift); } /** - * Spreads a NodeArrayNode into a NodeArray. + * Appends a node to an array. * - * @param source The source NodeArrayNode. - * @param dest The destination NodeArray. + * @param to The destination array. + * @param from The source Node or NodeArrayNode. * @param test The node test used to validate each node. */ - function spreadNodeArrayNode(source: NodeArrayNode, dest: NodeArray, test: (node: Node) => boolean) { - for (const element of source.nodes) { - if (element === undefined) { - continue; + export function addNode(to: T[], from: OneOrMore, test?: (node: Node) => boolean) { + if (to && from) { + if (isNodeArrayNode(from)) { + addNodes(to, from.nodes, test); + } + else { + Debug.assert(test === undefined || test(from), "Wrong node type after visit."); + to.push(from); } + } + } - Debug.assert(test === undefined || test(element), "Wrong node type after visit."); - dest.push(element); + /** + * Appends an array of nodes to an array. + * + * @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: OneOrMore[], test?: (node: Node) => boolean) { + if (to && from) { + for (const node of from) { + addNode(to, node, test); + } } } @@ -695,14 +716,31 @@ namespace ts { */ function mergeFunctionLikeLexicalEnvironment(node: FunctionLikeDeclaration, declarations: Statement[]) { Debug.assert(node.body !== undefined); - if (node.body.kind === SyntaxKind.Block) { - node.body = mergeLexicalEnvironment(node.body, declarations, /*nodeIsMutable*/ false); + node.body = mergeConciseBodyLexicalEnvironment(node.body, declarations); + } + + export function mergeFunctionBodyLexicalEnvironment(body: FunctionBody, declarations: Statement[], nodeIsMutable?: boolean): FunctionBody { + if (declarations && declarations.length > 0) { + return mergeLexicalEnvironment(body, declarations, nodeIsMutable); + } + + return body; + } + + export function mergeConciseBodyLexicalEnvironment(body: ConciseBody, declarations: Statement[], nodeIsMutable?: boolean): ConciseBody { + if (declarations && declarations.length > 0) { + if (isBlock(body)) { + return mergeLexicalEnvironment(body, declarations, nodeIsMutable); + } + else { + return createBlock([ + createReturn(body), + ...declarations + ]); + } } else { - node.body = createBlock([ - createReturn(node.body), - ...declarations - ]); + return body; } } @@ -717,7 +755,7 @@ namespace ts { * Merge generated declarations of a lexical environment into a NodeArray of Statement. */ function mergeStatements(statements: NodeArray, declarations: Statement[]) { - return createNodeArray(statements.concat(declarations), statements.pos, statements.end); + return createNodeArray(concatenate(statements, declarations), /*location*/ statements); } /** From 357171fb7cd194b37908d0db8eb078c9360e09ab Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 16 Feb 2016 16:12:00 -0800 Subject: [PATCH 04/43] PR feedback, switched to getEmitScriptTarget/getEmitModuleKind --- src/compiler/comments.ts | 6 +++--- src/compiler/printer.ts | 4 ++-- src/compiler/utilities.ts | 16 ---------------- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index 2b51e937fadd8..b93b144bf01b4 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -140,12 +140,12 @@ namespace ts { function emitLeadingComments(range: TextRange, leadingComments: CommentRange[] = getLeadingCommentsToEmit(range)) { emitNewLineBeforeLeadingComments(currentLineMap, writer, range, leadingComments); - // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space + // Leading comments are emitted as `/*leading comment1 */space/*leading comment*/space` emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); } function emitTrailingComments(range: TextRange, trailingComments = getTrailingCommentsToEmit(range)) { - // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ + // Trailing comments are emitted as `space/*trailing comment1 */space/*trailing comment*/` emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment); } @@ -182,7 +182,7 @@ namespace ts { // get the leading comments from detachedPos const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos; const leadingComments = getLeadingCommentRanges(currentText, pos); - if (detachedCommentsInfo.length - 1) { + if (detachedCommentsInfo.length > 1) { detachedCommentsInfo.pop(); } else { diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 0a707e335d15c..84388d4fd8895 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -76,8 +76,8 @@ function __export(m) { })`; const compilerOptions = host.getCompilerOptions(); - const languageVersion = getLanguageVersion(compilerOptions); - const moduleKind = getModuleKind(compilerOptions); + const languageVersion = getEmitScriptTarget(compilerOptions); + const moduleKind = getEmitModuleKind(compilerOptions); const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined; const emitterDiagnostics = createDiagnosticCollection(); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index d8025b8d1b90b..7026cafb0654a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -102,22 +102,6 @@ namespace ts { return true; } - export function getLanguageVersion(compilerOptions: CompilerOptions) { - return compilerOptions.target || ScriptTarget.ES3; - } - - export function getModuleKind(compilerOptions: CompilerOptions) { - if (compilerOptions.module) { - return compilerOptions.module; - } - - if (getLanguageVersion(compilerOptions) === ScriptTarget.ES6) { - return ModuleKind.ES6; - } - - return ModuleKind.None; - } - export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean { return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText); } From 6c0551f86788e3c3df305dbed99b2991c59a5802 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 16 Feb 2016 18:07:39 -0800 Subject: [PATCH 05/43] Adds initial jsx transformer --- src/compiler/factory.ts | 43 +++ src/compiler/transformers/jsx.ts | 462 ++++++++++++++++++++++++++++++- src/compiler/utilities.ts | 8 + 3 files changed, 511 insertions(+), 2 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 6e195314253af..0b0505bb50fef 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -514,6 +514,16 @@ namespace ts { return node; } + // Property assignments + + export function createPropertyAssignment(name: PropertyName, initializer: Expression) { + const node = createNode(SyntaxKind.PropertyAssignment); + node.name = name; + node.questionToken = undefined; + node.initializer = initializer; + return node; + } + // Compound nodes export function createAssignment(left: Expression, right: Expression, location?: TextRange) { @@ -573,6 +583,39 @@ namespace ts { ); } + export function createJsxSpread(reactNamespace: string, segments: Expression[]) { + return createCall( + createPropertyAccess( + createIdentifier(reactNamespace || "React"), + "__spread" + ), + segments + ); + } + + export function createJsxCreateElement(reactNamespace: string, tagName: Expression, props: Expression, children: Expression[]): LeftHandSideExpression { + const argumentsList = [tagName]; + if (props) { + argumentsList.push(props) + } + + if (children && children.length > 0) { + if (!props) { + argumentsList.push(createNull()); + } + + addRange(argumentsList, children); + } + + return createCall( + createPropertyAccess( + createIdentifier(reactNamespace || "React"), + "createElement" + ), + argumentsList + ); + } + // Helpers export function createParamHelper(expression: Expression, parameterOffset: number) { diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index e9470b32eea8f..8ad85f153c9c4 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -3,8 +3,10 @@ /*@internal*/ namespace ts { - // TODO(rbuckton): JSX->React transformer + const entities: Map = createEntitiesMap(); + export function transformJsx(context: TransformationContext) { + const compilerOptions = context.getCompilerOptions(); return transformSourceFile; function transformSourceFile(node: SourceFile) { @@ -24,7 +26,463 @@ namespace ts { } function visitorWorker(node: Node): Node { - return node; + switch (node.kind) { + case SyntaxKind.JsxElement: + return visitJsxElement(node); + + case SyntaxKind.JsxSelfClosingElement: + return visitJsxSelfClosingElement(node); + } + + Debug.fail("Unexpected node kind."); + } + + function transformJsxChildToExpression(node: JsxChild): Expression { + switch (node.kind) { + case SyntaxKind.JsxText: + return visitNonEmptyJsxText(node); + + case SyntaxKind.JsxExpression: + return visitJsxExpression(node); + + case SyntaxKind.JsxElement: + return visitJsxElement(node); + + case SyntaxKind.JsxSelfClosingElement: + return visitJsxSelfClosingElement(node); + } + + Debug.fail("Unexpected node kind."); + } + + 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[]) { + // We must the node onto the node stack if it is not already at the top. + const tagName = getTagName(node); + let objectProperties: Expression; + if (node.attributes.length === 0) { + // When there are no attributes, React wants "null" + objectProperties = createLiteral(null); + } + else { + // Either emit one big object literal (no spread attribs), or + // a call to React.__spread + const attrs = node.attributes; + if (forEach(attrs, isJsxSpreadAttribute)) { + const segments: Expression[] = []; + let properties: ObjectLiteralElement[] = []; + for (const attr of attrs) { + if (isJsxSpreadAttribute(attr)) { + if (properties) { + segments.push(createObjectLiteral(properties)); + properties = undefined; + } + + addNode(segments, transformJsxSpreadAttributeToExpression(attr), isExpression); + } + else { + if (!properties) { + properties = []; + } + + addNode(properties, transformJsxAttributeToObjectLiteralElement(attr), isObjectLiteralElement); + } + } + + if (properties) { + segments.push(createObjectLiteral(properties)); + } + + objectProperties = createJsxSpread(compilerOptions.reactNamespace, segments); + } + else { + const properties = map(node.attributes, transformJsxAttributeToObjectLiteralElement); + objectProperties = createObjectLiteral(properties); + } + } + + const childExpressions = filter(map(children, transformJsxChildToExpression), isDefined); + return createJsxCreateElement(compilerOptions.reactNamespace, tagName, objectProperties, childExpressions); + } + + 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 visitNonEmptyJsxText(node: JsxText) { + const text = getTextToEmit(node); + if (text !== undefined) { + return createLiteral(text); + } + return undefined; + } + + function getTextToEmit(node: JsxText) { + const text = trimReactWhitespaceAndApplyEntities(node); + if (text === undefined || text.length === 0) { + return undefined; + } + else { + return text; + } + } + + function trimReactWhitespaceAndApplyEntities(node: JsxText): string { + const text = getTextOfNode(node, /*includeTrivia*/ true); + let result: string = undefined; + 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)) { + let part = text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1); + result = (result ? result + "\" + ' ' + \"" : "") + part; + } + firstNonWhitespace = -1; + } + else if (!isWhiteSpace(c)) { + lastNonWhitespace = i; + if (firstNonWhitespace === -1) { + firstNonWhitespace = i; + } + } + } + + if (firstNonWhitespace !== -1) { + const part = text.substr(firstNonWhitespace); + result = (result ? result + "\" + ' ' + \"" : "") + part; + } + + if (result) { + // Replace entities like   + result = result.replace(/&(\w+);/g, function(s: any, m: string) { + if (entities[m] !== undefined) { + return String.fromCharCode(entities[m]); + } + else { + return s; + } + }); + } + + return result; } + + 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 visitJsxText(node: JsxText) { + const text = trimReactWhitespaceAndApplyEntities(node); + return createLiteral(text || ""); + } + + 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/utilities.ts b/src/compiler/utilities.ts index a5a0a05a0be12..b84feea4153fa 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -180,6 +180,10 @@ namespace ts { return node.pos; } + export function isDefined(value: any): boolean { + return value !== undefined; + } + // Returns true if this node is missing from the actual source code. A 'missing' node is different // from 'undefined/defined'. When a node is undefined (which can happen for optional nodes // in the tree), it is definitely missing. However, a node may be defined, but still be @@ -3237,6 +3241,10 @@ namespace ts { || kind === SyntaxKind.JsxSpreadAttribute; } + export function isJsxSpreadAttribute(node: Node): node is JsxSpreadAttribute { + return node.kind === SyntaxKind.JsxSpreadAttribute; + } + // Clauses export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause { From 1ceb02a5bcac40df2e69e467c75ce26905545af4 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 16 Feb 2016 18:10:52 -0800 Subject: [PATCH 06/43] Added initial ES7 transformer --- src/compiler/transformers/es7.ts | 69 +++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts index e4d1e564cb8b3..9dca322c17515 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -3,7 +3,6 @@ /*@internal*/ namespace ts { - // TODO(rbuckton): ES7->ES6 transformer export function transformES7(context: TransformationContext) { const { hoistVariableDeclaration } = context; @@ -26,7 +25,73 @@ namespace ts { } function visitorWorker(node: Node) { - return node; + switch (node.kind) { + case SyntaxKind.BinaryExpression: + return visitBinaryExpression(node); + } + + Debug.fail("Unexpected node kind."); + } + + 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.fail("Unexpected node kind."); + } } } } \ No newline at end of file From dd2dc78dd6d1ee723beddc5e62e0818ca6452ffd Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 19 Feb 2016 16:59:57 -0800 Subject: [PATCH 07/43] Enables source maps for exceptions thrown in the compiler (when available) --- src/compiler/sys.ts | 11 +++++++++++ src/compiler/tsc.ts | 4 ++++ 2 files changed, 15 insertions(+) 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/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); From c759b633d6e33754fb0cd4473759062ce920c4a2 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 19 Feb 2016 17:01:02 -0800 Subject: [PATCH 08/43] Adds ES6 transformer --- Jakefile.js | 9 + src/compiler/binder.ts | 36 +- src/compiler/checker.ts | 10 +- src/compiler/commandLineParser.ts | 5 + src/compiler/comments.ts | 138 +- src/compiler/core.ts | 16 +- src/compiler/declarationEmitter.ts | 8 +- src/compiler/emitter.ts | 66 +- src/compiler/factory.ts | 343 ++++- src/compiler/parser.ts | 4 + src/compiler/printer.ts | 237 +-- src/compiler/program.ts | 2 +- src/compiler/scanner.ts | 5 +- src/compiler/sourcemap.ts | 4 +- src/compiler/transformer.ts | 7 +- src/compiler/transformers/destructuring.ts | 10 +- src/compiler/transformers/es6.ts | 1515 +++++++++++++++++++- src/compiler/transformers/es7.ts | 4 +- src/compiler/transformers/jsx.ts | 8 +- src/compiler/transformers/ts.ts | 147 +- src/compiler/types.ts | 36 +- src/compiler/utilities.ts | 182 ++- src/compiler/visitor.ts | 158 +- 23 files changed, 2529 insertions(+), 421 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 69510545ea320..1929a3a475f72 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -238,6 +238,7 @@ function concatenateFiles(destinationFile, sourceFiles) { } var useDebugMode = true; +var useTransforms = process.env.USE_TRANSFORMS || false; var host = (process.env.host || process.env.TYPESCRIPT_HOST || "node"); var compilerFilename = "tsc.js"; var LKGCompiler = path.join(LKGDirectory, compilerFilename); @@ -297,6 +298,10 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, noOu options += " --stripInternal" } + if (useBuiltCompiler && useTransforms) { + options += " --experimentalTransforms" + } + var cmd = host + " " + compilerPath + " " + options + " "; cmd = cmd + sources.join(" "); console.log(cmd + "\n"); @@ -420,6 +425,10 @@ task("setDebugMode", function() { useDebugMode = true; }); +task("setTransforms", function() { + useTransforms = true; +}); + task("configure-nightly", [configureNightlyJs], function() { var cmd = host + " " + configureNightlyJs + " " + packageJson + " " + programTs; console.log(cmd); diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b0ae5804b5fea..6f1de3677e730 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1971,20 +1971,6 @@ namespace ts { break; - case SyntaxKind.ExpressionStatement: - if (nodeIsSynthesized(node)) { - const expression = (node).expression; - if (nodeIsSynthesized(expression) - && isCallExpression(expression) - && expression.expression.kind === SyntaxKind.SuperKeyword) { - // A synthesized call to `super` should be transformed to a cleaner emit - // when transpiling to ES5/3. - transformFlags |= TransformFlags.AssertES6; - } - } - - break; - case SyntaxKind.BinaryExpression: if (isDestructuringAssignment(node)) { // Destructuring assignments are ES6 syntax. @@ -2093,7 +2079,7 @@ namespace ts { case SyntaxKind.VariableDeclarationList: // If a VariableDeclarationList is `let` or `const`, then it is ES6 syntax. if (node.flags & NodeFlags.BlockScoped) { - transformFlags |= TransformFlags.AssertES6; + transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsBlockScopedBinding; } break; @@ -2106,6 +2092,26 @@ namespace ts { break; + 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; + + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + // A loop containing a block scoped binding *may* need to be transformed from ES6. + if (subtreeFlags & TransformFlags.ContainsBlockScopedBinding) { + transformFlags |= TransformFlags.AssertES6; + } + + break; + case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: // A ClassDeclarations or ClassExpression is ES6 syntax. diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3cb9031fd9913..7dfd679e72c26 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16672,11 +16672,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 diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index d5bf95a64058a..c65466f00dede 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -320,6 +320,11 @@ namespace ts { name: "allowSyntheticDefaultImports", type: "boolean", description: Diagnostics.Allow_default_imports_from_modules_with_no_default_export_This_does_not_affect_code_emit_just_typechecking + }, + { + name: "experimentalTransforms", + type: "boolean", + experimental: true } ]; diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index 2b51e937fadd8..a680d01e102a1 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -5,11 +5,15 @@ namespace ts { export interface CommentWriter { reset(): void; setSourceFile(sourceFile: SourceFile): void; - getLeadingCommentsToEmit(node: TextRange): CommentRange[]; - getTrailingCommentsToEmit(node: TextRange): CommentRange[]; - emitDetachedComments(node: TextRange): void; - emitLeadingComments(node: TextRange, comments?: CommentRange[]): void; - emitTrailingComments(node: TextRange, comments?: CommentRange[]): void; + getLeadingComments(range: Node, getAdditionalRange?: (range: Node) => Node): CommentRange[]; + getLeadingComments(range: TextRange): CommentRange[]; + getLeadingCommentsOfPosition(pos: number): CommentRange[]; + getTrailingComments(range: Node, getAdditionalRange?: (range: Node) => Node): CommentRange[]; + getTrailingComments(range: TextRange): CommentRange[]; + getTrailingCommentsOfPosition(pos: number): CommentRange[]; + emitLeadingComments(range: TextRange, comments?: CommentRange[]): void; + emitTrailingComments(range: TextRange, comments?: CommentRange[]): void; + emitDetachedComments(range: TextRange): void; } export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter { @@ -25,8 +29,8 @@ namespace ts { // This maps start->end for a comment range. See `hasConsumedCommentRange` and // `consumeCommentRange` for usage. let consumedCommentRanges: number[]; - let leadingCommentRangeNodeStarts: boolean[]; - let trailingCommentRangeNodeEnds: boolean[]; + let leadingCommentRangePositions: boolean[]; + let trailingCommentRangePositions: boolean[]; return compilerOptions.removeComments ? createCommentRemovingWriter() @@ -36,11 +40,13 @@ namespace ts { return { reset, setSourceFile, - getLeadingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; }, - getTrailingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; }, + getLeadingComments(range: TextRange, getAdditionalRange?: (range: TextRange) => TextRange): CommentRange[] { return undefined; }, + getLeadingCommentsOfPosition(pos: number): CommentRange[] { return undefined; }, + getTrailingComments(range: TextRange, getAdditionalRange?: (range: TextRange) => TextRange): CommentRange[] { return undefined; }, + getTrailingCommentsOfPosition(pos: number): CommentRange[] { return undefined; }, + emitLeadingComments(range: TextRange, comments?: CommentRange[]): void { }, + emitTrailingComments(range: TextRange, comments?: CommentRange[]): void { }, emitDetachedComments, - emitLeadingComments(node: TextRange, comments?: CommentRange[]): void { }, - emitTrailingComments(node: TextRange, comments?: CommentRange[]): void { }, }; function emitDetachedComments(node: TextRange): void { @@ -53,41 +59,85 @@ namespace ts { return { reset, setSourceFile, - getLeadingCommentsToEmit, - getTrailingCommentsToEmit, - emitDetachedComments, + getLeadingComments, + getLeadingCommentsOfPosition, + getTrailingComments, + getTrailingCommentsOfPosition, emitLeadingComments, emitTrailingComments, + emitDetachedComments, }; - function getLeadingCommentsToEmit(node: TextRange) { - if (nodeIsSynthesized(node)) { - return; + function getLeadingComments(range: TextRange | Node, getAdditionalRange?: (range: Node) => Node) { + let comments = getLeadingCommentsOfPosition(range.pos); + if (getAdditionalRange) { + let additionalRange = getAdditionalRange(range); + while (additionalRange) { + comments = concatenate( + getLeadingCommentsOfPosition(additionalRange.pos), + comments + ); + + additionalRange = getAdditionalRange(additionalRange); + } } - if (!leadingCommentRangeNodeStarts[node.pos]) { - leadingCommentRangeNodeStarts[node.pos] = true; - const comments = hasDetachedComments(node.pos) - ? getLeadingCommentsWithoutDetachedComments() - : getLeadingCommentRanges(currentText, node.pos); - return consumeCommentRanges(comments); + return comments; + } + + function getTrailingComments(range: TextRange | Node, getAdditionalRange?: (range: Node) => Node) { + let comments = getTrailingCommentsOfPosition(range.end); + if (getAdditionalRange) { + let additionalRange = getAdditionalRange(range); + while (additionalRange) { + comments = concatenate( + comments, + getTrailingCommentsOfPosition(additionalRange.end) + ); + + additionalRange = getAdditionalRange(additionalRange); + } } - return noComments; + return comments; } - function getTrailingCommentsToEmit(node: TextRange) { - if (nodeIsSynthesized(node)) { - return; + function getLeadingCommentsOfPosition(pos: number) { + if (positionIsSynthesized(pos) || leadingCommentRangePositions[pos]) { + return undefined; } - if (!trailingCommentRangeNodeEnds[node.end]) { - trailingCommentRangeNodeEnds[node.end] = true; - const comments = getTrailingCommentRanges(currentText, node.end); - return consumeCommentRanges(comments); + leadingCommentRangePositions[pos] = true; + const comments = hasDetachedComments(pos) + ? getLeadingCommentsWithoutDetachedComments() + : getLeadingCommentRanges(currentText, pos); + return consumeCommentRanges(comments); + } + + function getTrailingCommentsOfPosition(pos: number) { + if (positionIsSynthesized(pos) || trailingCommentRangePositions[pos]) { + return undefined; } - return noComments; + trailingCommentRangePositions[pos] = true; + const comments = getTrailingCommentRanges(currentText, pos); + return consumeCommentRanges(comments); + } + + function emitLeadingComments(range: TextRange, comments = getLeadingComments(range)) { + emitNewLineBeforeLeadingComments(currentLineMap, writer, range, comments); + + // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space + emitComments(currentText, currentLineMap, writer, comments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); + } + + function emitTrailingComments(range: TextRange, comments = getTrailingComments(range)) { + // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ + emitComments(currentText, currentLineMap, writer, comments, /*leadingSeparator*/ true, /*trailingSeparator*/ false, newLine, writeComment); + } + + function emitDetachedComments(range: TextRange) { + emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false); } function hasConsumedCommentRange(comment: CommentRange) { @@ -136,22 +186,6 @@ namespace ts { return noComments; } - - function emitLeadingComments(range: TextRange, leadingComments: CommentRange[] = getLeadingCommentsToEmit(range)) { - emitNewLineBeforeLeadingComments(currentLineMap, writer, range, leadingComments); - - // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); - } - - function emitTrailingComments(range: TextRange, trailingComments = getTrailingCommentsToEmit(range)) { - // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ - emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment); - } - - function emitDetachedComments(range: TextRange) { - emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false); - } } function reset() { @@ -160,8 +194,8 @@ namespace ts { currentLineMap = undefined; detachedCommentsInfo = undefined; consumedCommentRanges = undefined; - trailingCommentRangeNodeEnds = undefined; - leadingCommentRangeNodeStarts = undefined; + trailingCommentRangePositions = undefined; + leadingCommentRangePositions = undefined; } function setSourceFile(sourceFile: SourceFile) { @@ -170,8 +204,8 @@ namespace ts { currentLineMap = getLineStarts(sourceFile); detachedCommentsInfo = undefined; consumedCommentRanges = []; - leadingCommentRangeNodeStarts = []; - trailingCommentRangeNodeEnds = []; + leadingCommentRangePositions = []; + trailingCommentRangePositions = []; } function hasDetachedComments(pos: number) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 19726482cb3c1..09446cf668d6c 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -292,9 +292,9 @@ namespace ts { return ~low; } - export function reduceLeft(array: T[], f: (memo: U, value: T) => U, initial: U): U; - export function reduceLeft(array: T[], f: (memo: T, value: T) => T): T; - export function reduceLeft(array: T[], f: (memo: T, value: T) => T, initial?: T): T { + export function reduceLeft(array: T[], f: (memo: U, value: T, i: number) => U, initial: U): U; + export function reduceLeft(array: T[], f: (memo: T, value: T, i: number) => T): T; + export function reduceLeft(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T): T { if (array) { const count = array.length; if (count > 0) { @@ -308,7 +308,7 @@ namespace ts { result = initial; } while (pos < count) { - result = f(result, array[pos]); + result = f(result, array[pos], pos); pos++; } return result; @@ -317,9 +317,9 @@ namespace ts { return initial; } - export function reduceRight(array: T[], f: (memo: U, value: T) => U, initial: U): U; - export function reduceRight(array: T[], f: (memo: T, value: T) => T): T; - export function reduceRight(array: T[], f: (memo: T, value: T) => T, initial?: T): T { + export function reduceRight(array: T[], f: (memo: U, value: T, i: number) => U, initial: U): U; + export function reduceRight(array: T[], f: (memo: T, value: T, i: number) => T): T; + export function reduceRight(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T): T { if (array) { let pos = array.length - 1; if (pos >= 0) { @@ -332,7 +332,7 @@ namespace ts { result = initial; } while (pos >= 0) { - result = f(result, array[pos]); + result = f(result, array[pos], pos); pos--; } return result; diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index ab0b16947fc49..c89b7579d5247 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -91,7 +91,7 @@ namespace ts { // Emit reference in dts, if the file reference was not already emitted if (referencedFile && !contains(emittedReferencedFiles, referencedFile)) { // Add a reference to generated dts file, - // global file reference is added only + // global file reference is added only // - if it is not bundled emit (because otherwise it would be self reference) // - and it is not already added if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference)) { @@ -144,7 +144,7 @@ namespace ts { if (!isBundledEmit && isExternalModule(sourceFile) && sourceFile.moduleAugmentations.length && !resultHasExternalModuleIndicator) { // if file was external module with augmentations - this fact should be preserved in .d.ts as well. - // in case if we didn't write any external module specifiers in .d.ts we need to emit something + // in case if we didn't write any external module specifiers in .d.ts we need to emit something // that will force compiler to think that this file is an external module - 'export {}' is a reasonable choice here. write("export {};"); writeLine(); @@ -349,7 +349,7 @@ namespace ts { const jsDocComments = getJsDocCommentsFromText(declaration, currentText); emitNewLineBeforeLeadingComments(currentLineMap, writer, declaration, jsDocComments); // jsDoc comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, jsDocComments, /*trailingSeparator*/ true, newLine, writeCommentRange); + emitComments(currentText, currentLineMap, writer, jsDocComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeCommentRange); } } @@ -736,7 +736,7 @@ namespace ts { function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) { // emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations). - // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered + // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered // external modules since they are indistingushable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}' // so compiler will treat them as external modules. resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ebac29967655d..3919e68aba5d9 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -287,12 +287,8 @@ namespace ts { _i = 0x10000000, // Use/preference flag for '_i' } - export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { - return printFiles(resolver, host, targetSourceFile); - } - // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature - export function legacyEmitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { + export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { // emit output for the __extends helper function const extendsHelper = ` var __extends = (this && this.__extends) || function (d, b) { @@ -1919,7 +1915,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge if (multiLine) { decreaseIndent(); - writeLine(); } write(")"); @@ -2246,6 +2241,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return node; } + function skipAssertions(node: Expression): Expression { + while (node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression) { + node = (node).expression; + } + return node; + } + function emitCallTarget(node: Expression): Expression { if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword) { emit(node); @@ -2695,7 +2697,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } function synthesizedNodeStartsOnNewLine(node: Node) { - return nodeIsSynthesized(node) && (node).startsOnNewLine; + return nodeIsSynthesized(node) && node.startsOnNewLine; } function emitConditionalExpression(node: ConditionalExpression) { @@ -3312,8 +3314,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge // we can't reuse 'arr' because it might be modified within the body of the loop. const counter = createTempVariable(TempFlags._i); const rhsReference = createSynthesizedNode(SyntaxKind.Identifier) as Identifier; - rhsReference.text = node.expression.kind === SyntaxKind.Identifier ? - makeUniqueName((node.expression).text) : + const expressionWithoutAssertions = skipAssertions(node.expression); + rhsReference.text = expressionWithoutAssertions.kind === SyntaxKind.Identifier ? + makeUniqueName((expressionWithoutAssertions).text) : makeTempVariableName(TempFlags.Auto); // This is the let keyword for the counter and rhsReference. The let keyword for @@ -4328,7 +4331,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge writeLine(); emitStart(restParam); emitNodeWithCommentsAndWithoutSourcemap(restParam.name); - write("[" + tempName + " - " + restIndex + "] = arguments[" + tempName + "];"); + write(restIndex > 0 + ? `[${tempName} - ${restIndex}] = arguments[${tempName}];` + : `[${tempName}] = arguments[${tempName}];`); emitEnd(restParam); decreaseIndent(); writeLine(); @@ -5344,6 +5349,18 @@ const _super = (function (geti, seti) { write(" = "); } + const staticProperties = getInitializedProperties(node, /*isStatic*/ true); + const isClassExpressionWithStaticProperties = staticProperties.length > 0 && node.kind === SyntaxKind.ClassExpression; + let tempVariable: Identifier; + + if (isClassExpressionWithStaticProperties) { + tempVariable = createAndRecordTempVariable(TempFlags.Auto); + write("("); + increaseIndent(); + emit(tempVariable); + write(" = "); + } + write("(function ("); const baseTypeNode = getClassExtendsHeritageClauseElement(node); if (baseTypeNode) { @@ -5373,9 +5390,6 @@ const _super = (function (geti, seti) { writeLine(); emitConstructor(node, baseTypeNode); emitMemberFunctionsForES5AndLower(node); - emitPropertyDeclarations(node, getInitializedProperties(node, /*isStatic*/ true)); - writeLine(); - emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); writeLine(); emitToken(SyntaxKind.CloseBraceToken, node.members.end, () => { write("return "); @@ -5402,7 +5416,23 @@ const _super = (function (geti, seti) { write("))"); if (node.kind === SyntaxKind.ClassDeclaration) { write(";"); + emitPropertyDeclarations(node, staticProperties); + writeLine(); + emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); } + else if (isClassExpressionWithStaticProperties) { + for (const property of staticProperties) { + write(","); + writeLine(); + emitPropertyDeclaration(node, property, /*receiver*/ tempVariable, /*isExpression*/ true); + } + write(","); + writeLine(); + emit(tempVariable); + decreaseIndent(); + write(")"); + } + emitEnd(node); if (node.kind === SyntaxKind.ClassDeclaration) { @@ -7941,7 +7971,7 @@ const _super = (function (geti, seti) { emitNewLineBeforeLeadingComments(currentLineMap, writer, node, leadingComments); // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); + emitComments(currentText, currentLineMap, writer, leadingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); } function emitTrailingComments(node: Node) { @@ -7953,7 +7983,7 @@ const _super = (function (geti, seti) { const trailingComments = getTrailingCommentsToEmit(node); // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ - emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment); + emitComments(currentText, currentLineMap, writer, trailingComments, /*leadingSeparator*/ true, /*trailingSeparator*/ false, newLine, writeComment); } /** @@ -7968,8 +7998,8 @@ const _super = (function (geti, seti) { const trailingComments = getTrailingCommentRanges(currentText, pos); - // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ - emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ true, newLine, writeComment); + // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space + emitComments(currentText, currentLineMap, writer, trailingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); } function emitLeadingCommentsOfPositionWorker(pos: number) { @@ -7990,7 +8020,7 @@ const _super = (function (geti, seti) { emitNewLineBeforeLeadingComments(currentLineMap, writer, { pos: pos, end: pos }, leadingComments); // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); + emitComments(currentText, currentLineMap, writer, leadingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); } function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 0b0505bb50fef..5fdffc32d46de 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -92,7 +92,7 @@ namespace ts { } export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node { - const node = createNode(kind, /*location*/ undefined); + const node = createNode(kind, /*location*/ undefined); node.startsOnNewLine = startsOnNewLine; return node; } @@ -153,20 +153,20 @@ namespace ts { // Literals - export function createLiteral(value: string): StringLiteral; - export function createLiteral(value: number): LiteralExpression; - export function createLiteral(value: string | number | boolean): PrimaryExpression; - export function createLiteral(value: string | number | boolean): PrimaryExpression { + export function createLiteral(value: string, location?: TextRange): StringLiteral; + export function createLiteral(value: number, location?: TextRange): LiteralExpression; + export function createLiteral(value: string | number | boolean, location?: TextRange): PrimaryExpression; + export function createLiteral(value: string | number | boolean, location?: TextRange): PrimaryExpression { if (typeof value === "number") { - const node = createNode(SyntaxKind.NumericLiteral); + const node = createNode(SyntaxKind.NumericLiteral, location); node.text = value.toString(); return node; } else if (typeof value === "boolean") { - return createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword); + return createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword, location); } else { - const node = createNode(SyntaxKind.StringLiteral); + const node = createNode(SyntaxKind.StringLiteral, location); node.text = String(value); return node; } @@ -203,8 +203,8 @@ namespace ts { return node; } - export function createThis() { - const node = createNode(SyntaxKind.ThisKeyword); + export function createThis(location?: TextRange) { + const node = createNode(SyntaxKind.ThisKeyword, location); return node; } @@ -267,8 +267,8 @@ namespace ts { return node; } - export function createParameter(name: string | Identifier | BindingPattern, initializer?: Expression) { - const node = createNode(SyntaxKind.Parameter); + export function createParameter(name: string | Identifier | BindingPattern, initializer?: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.Parameter, location); node.decorators = undefined; node.modifiers = undefined; node.dotDotDotToken = undefined; @@ -288,8 +288,8 @@ namespace ts { return node; } - export function createObjectLiteral(properties?: ObjectLiteralElement[]) { - const node = createNode(SyntaxKind.ObjectLiteralExpression); + export function createObjectLiteral(properties?: ObjectLiteralElement[], location?: TextRange) { + const node = createNode(SyntaxKind.ObjectLiteralExpression, location); node.properties = createNodeArray(properties); return node; } @@ -316,6 +316,13 @@ namespace ts { return node; } + export function createNew(expression: Expression, argumentsArray: Expression[], location?: TextRange) { + const node = createNode(SyntaxKind.NewExpression, location); + node.expression = parenthesizeForAccess(expression); + node.arguments = argumentsArray ? createNodeArray(argumentsArray) : undefined; + return node; + } + export function createParen(expression: Expression, location?: TextRange) { const node = createNode(SyntaxKind.ParenthesizedExpression, location); node.expression = expression; @@ -347,13 +354,20 @@ namespace ts { export function createTypeOf(expression: Expression) { const node = createNode(SyntaxKind.TypeOfExpression); - node.expression = parenthesizeForUnary(expression); + node.expression = parenthesizePrefixOperand(expression); return node; } export function createVoid(expression: Expression) { const node = createNode(SyntaxKind.VoidExpression); - node.expression = parenthesizeForUnary(expression); + node.expression = parenthesizePrefixOperand(expression); + return node; + } + + export function createPostfix(operand: Expression, operator: SyntaxKind, location?: TextRange) { + const node = createNode(SyntaxKind.PostfixUnaryExpression, location); + node.operand = parenthesizePostfixOperand(operand); + node.operator = operator; return node; } @@ -442,14 +456,64 @@ namespace ts { return node; } + export function createEmptyStatement(location: TextRange) { + return createNode(SyntaxKind.EmptyStatement, location); + } + export function createStatement(expression: Expression, location?: TextRange): ExpressionStatement { const node = createNode(SyntaxKind.ExpressionStatement, location); node.expression = expression; return node; } - export function createReturn(expression?: Expression): ReturnStatement { - const node = createSynthesizedNode(SyntaxKind.ReturnStatement); + export function createIf(expression: Expression, thenStatement: Statement, elseStatement?: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.IfStatement, location); + node.expression = expression; + node.thenStatement = thenStatement; + node.elseStatement = elseStatement; + return node; + } + + export function createFor(initializer: ForInitializer, condition: Expression, incrementor: Expression, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.ForStatement, location); + node.initializer = initializer; + node.condition = condition; + node.incrementor = incrementor; + node.statement = statement; + return node; + } + + export function createLabel(label: string | Identifier, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.LabeledStatement, location); + node.label = typeof label === "string" ? createIdentifier(label) : label; + node.statement = statement; + return node; + } + + export function createDo(expression: Expression, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.DoStatement, location); + node.expression = expression; + node.statement = statement; + return node; + } + + export function createWhile(statement: Statement, expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.WhileStatement, location); + node.statement = statement; + node.expression = expression; + return node; + } + + export function createForIn(initializer: ForInitializer, expression: Expression, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.ForInStatement, location); + node.initializer = initializer; + node.expression = expression; + node.statement = statement; + return node; + } + + export function createReturn(expression?: Expression, location?: TextRange): ReturnStatement { + const node = createNode(SyntaxKind.ReturnStatement, location); node.expression = expression; return node; } @@ -516,9 +580,9 @@ namespace ts { // Property assignments - export function createPropertyAssignment(name: PropertyName, initializer: Expression) { - const node = createNode(SyntaxKind.PropertyAssignment); - node.name = name; + export function createPropertyAssignment(name: string | PropertyName, initializer: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.PropertyAssignment, location); + node.name = typeof name === "string" ? createIdentifier(name) : name; node.questionToken = undefined; node.initializer = initializer; return node; @@ -526,16 +590,16 @@ namespace ts { // Compound nodes - export function createAssignment(left: Expression, right: Expression, location?: TextRange) { - return createBinary(left, SyntaxKind.EqualsToken, right, location); + export function createComma(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.CommaToken, right); } - export function createLogicalAnd(left: Expression, right: Expression) { - return createBinary(left, SyntaxKind.AmpersandAmpersandToken, right); + export function createLessThan(left: Expression, right: Expression, location?: TextRange) { + return createBinary(left, SyntaxKind.LessThanToken, right, location); } - export function createLogicalOr(left: Expression, right: Expression) { - return createBinary(left, SyntaxKind.BarBarToken, right); + export function createAssignment(left: Expression, right: Expression, location?: TextRange) { + return createBinary(left, SyntaxKind.EqualsToken, right, location); } export function createStrictEquality(left: Expression, right: Expression) { @@ -546,8 +610,24 @@ namespace ts { return createBinary(left, SyntaxKind.ExclamationEqualsEqualsToken, right); } - export function createComma(left: Expression, right: Expression) { - return createBinary(left, SyntaxKind.CommaToken, right); + export function createAdd(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.PlusToken, right); + } + + export function createSubtract(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.MinusToken, right); + } + + export function createPostfixIncrement(operand: Expression, location?: TextRange) { + return createPostfix(operand, SyntaxKind.PlusPlusToken, location); + } + + export function createLogicalAnd(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.AmpersandAmpersandToken, right); + } + + export function createLogicalOr(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.BarBarToken, right); } export function createVoidZero() { @@ -566,6 +646,28 @@ namespace ts { return node; } + export function createFunctionCall(func: Expression, thisArg: Expression, argumentsList: Expression[], location?: TextRange) { + return createCall( + createPropertyAccess(func, "call"), + [ + thisArg, + ...argumentsList + ], + location + ); + } + + export function createFunctionApply(func: Expression, thisArg: Expression, argumentsExpression: Expression, location?: TextRange) { + return createCall( + createPropertyAccess(func, "apply"), + [ + thisArg, + argumentsExpression + ], + location + ); + } + export function createArraySlice(array: Expression, start?: number | Expression) { const argumentsList: Expression[] = []; if (start !== undefined) { @@ -575,6 +677,13 @@ namespace ts { return createCall(createPropertyAccess(array, "slice"), argumentsList); } + export function createArrayConcat(array: Expression, values: Expression[]) { + return createCall( + createPropertyAccess(array, "concat"), + values + ); + } + export function createMathPow(left: Expression, right: Expression, location?: TextRange) { return createCall( createPropertyAccess(createIdentifier("Math"), "pow"), @@ -618,6 +727,16 @@ namespace ts { // Helpers + export function createExtendsHelper(name: Identifier) { + return createCall( + createIdentifier("__extends"), + [ + name, + createIdentifier("_super") + ] + ); + } + export function createParamHelper(expression: Expression, parameterOffset: number) { return createCall( createIdentifier("__param"), @@ -671,6 +790,56 @@ namespace ts { ); } + function createPropertyDescriptor({ get, set, value, enumerable, configurable, writable }: PropertyDescriptorOptions, preferNewLine?: boolean, location?: TextRange) { + const properties: ObjectLiteralElement[] = []; + addPropertyAssignment(properties, "get", get, preferNewLine); + addPropertyAssignment(properties, "set", set, preferNewLine); + addPropertyAssignment(properties, "value", value, preferNewLine); + addPropertyAssignment(properties, "enumerable", enumerable, preferNewLine); + addPropertyAssignment(properties, "configurable", configurable, preferNewLine); + addPropertyAssignment(properties, "writable", writable, preferNewLine); + return createObjectLiteral(properties, location) + } + + function addPropertyAssignment(properties: ObjectLiteralElement[], name: string, value: boolean | Expression, preferNewLine: boolean) { + if (value !== undefined) { + const property = createPropertyAssignment( + name, + typeof value === "boolean" ? createLiteral(value) : value + ); + + if (preferNewLine) { + startOnNewLine(property); + } + + addNode(properties, property); + } + } + + export interface PropertyDescriptorOptions { + get?: Expression; + set?: Expression; + value?: Expression; + enumerable?: boolean | Expression; + configurable?: boolean | Expression; + writable?: boolean | Expression; + } + + export function createObjectDefineProperty(target: Expression, memberName: Expression, descriptor: PropertyDescriptorOptions, preferNewLine?: boolean, location?: TextRange) { + return createCall( + createPropertyAccess( + createIdentifier("Object"), + "defineProperty" + ), + [ + target, + memberName, + createPropertyDescriptor(descriptor, preferNewLine) + ], + location + ); + } + function createObjectCreate(prototype: Expression) { return createCall( createPropertyAccess(createIdentifier("Object"), "create"), @@ -839,6 +1008,12 @@ namespace ts { : cloneNode(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); + } + // Utilities /** @@ -850,11 +1025,7 @@ namespace ts { * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the * BinaryExpression. */ - function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean) { - // When diagnosing whether the expression needs parentheses, the decision should be based - // on the innermost expression in a chain of nested type assertions. - operand = skipAssertions(operand); - + export function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean) { // If the resulting expression is already parenthesized, we do not need to do any further processing. if (operand.kind === SyntaxKind.ParenthesizedExpression) { return operand; @@ -968,11 +1139,7 @@ namespace ts { * * @param expr The expression node. */ - function parenthesizeForAccess(expr: Expression): LeftHandSideExpression { - // When diagnosing whether the expression needs parentheses, the decision should be based - // on the innermost expression in a chain of nested type assertions. - expr = skipAssertions(expr); - + export function parenthesizeForAccess(expression: Expression): LeftHandSideExpression { // isLeftHandSideExpression is almost the correct criterion for when it is not necessary // to parenthesize the expression before a dot. The known exceptions are: // @@ -981,21 +1148,86 @@ namespace ts { // NumericLiteral // 1.x -> not the same as (1).x // - if (isLeftHandSideExpression(expr) && - expr.kind !== SyntaxKind.NewExpression && - expr.kind !== SyntaxKind.NumericLiteral) { - return expr; + if (isLeftHandSideExpression(expression) && + expression.kind !== SyntaxKind.NewExpression && + expression.kind !== SyntaxKind.NumericLiteral) { + return expression; } - return createParen(expr); + return createParen(expression, /*location*/ expression); } - function parenthesizeForUnary(operand: Expression) { - if (isUnaryExpression(operand)) { - return operand; + export function parenthesizePostfixOperand(operand: Expression) { + return isLeftHandSideExpression(operand) + ? operand + : createParen(operand, /*location*/ operand); + } + + export function parenthesizePrefixOperand(operand: Expression) { + return isUnaryExpression(operand) + ? operand + : createParen(operand, /*location*/ operand); + } + + export function parenthesizeExpressionForList(expression: Expression) { + const expressionPrecedence = getExpressionPrecedence(expression); + const commaPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, SyntaxKind.CommaToken); + return expressionPrecedence > commaPrecedence + ? expression + : createParen(expression, /*location*/ expression); + } + + export function parenthesizeExpressionForExpressionStatement(expression: Expression) { + if (isCallExpression(expression)) { + const callee = expression.expression; + if (callee.kind === SyntaxKind.FunctionExpression + || callee.kind === SyntaxKind.ArrowFunction) { + const clone = cloneNode(expression, expression, expression.flags, expression.parent, expression); + clone.expression = createParen(callee, /*location*/ callee); + return clone; + } + } + else if (getLeftmostExpression(expression).kind === SyntaxKind.ObjectLiteralExpression) { + return createParen(expression, /*location*/ expression); + } + + return expression; + } + + function getLeftmostExpression(node: Expression): Expression { + while (true) { + switch (node.kind) { + case SyntaxKind.PostfixUnaryExpression: + node = (node).operand; + continue; + + case SyntaxKind.BinaryExpression: + node = (node).left; + continue; + + case SyntaxKind.ConditionalExpression: + node = (node).condition; + continue; + + case SyntaxKind.CallExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.PropertyAccessExpression: + node = (node).expression; + continue; + } + + return node; } + } - return createParen(operand); + export function skipParentheses(node: Expression): Expression { + while (node.kind === SyntaxKind.ParenthesizedExpression + || node.kind === SyntaxKind.TypeAssertionExpression + || node.kind === SyntaxKind.AsExpression) { + node = (node).expression; + } + + return node; } /** @@ -1004,7 +1236,8 @@ namespace ts { * @param node The expression node. */ function skipAssertions(node: Expression) { - while (node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression) { + while (node.kind === SyntaxKind.TypeAssertionExpression + || node.kind === SyntaxKind.AsExpression) { node = (node).expression; } @@ -1012,7 +1245,7 @@ namespace ts { } export function startOnNewLine(node: T): T { - (node).startsOnNewLine = true; + node.startsOnNewLine = true; return node; } @@ -1034,6 +1267,16 @@ namespace ts { 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; + } + export function getSynthesizedNode(node: T): T { return nodeIsSynthesized(node) ? node : cloneNode(node, /*location*/ undefined, node.flags, /*parent*/ undefined, /*original*/ node); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b21f9919f4575..c4813ac993217 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4069,6 +4069,10 @@ namespace ts { function parseBlock(ignoreMissingOpenBrace: boolean, diagnosticMessage?: DiagnosticMessage): Block { const node = createNode(SyntaxKind.Block); if (parseExpected(SyntaxKind.OpenBraceToken, diagnosticMessage) || ignoreMissingOpenBrace) { + if (scanner.hasPrecedingLineBreak()) { + node.multiLine = true; + } + node.statements = parseList(ParsingContext.BlockStatements, parseStatement); parseExpected(SyntaxKind.CloseBraceToken); } diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 8a0725f52368e..8b3a69b104a28 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -119,13 +119,31 @@ const _super = (function (geti, seti) { const transformers = getTransformers(compilerOptions).concat(initializePrinter); const writer = createTextWriter(newLine); - const { write, writeTextOfNode, writeLine, increaseIndent, decreaseIndent } = writer; + const { + write, + writeTextOfNode, + writeLine, + increaseIndent, + decreaseIndent + } = writer; const sourceMap = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? createSourceMapWriter(host, writer) : getNullSourceMapWriter(); - const { emitStart, emitEnd, emitPos } = sourceMap; + const { + emitStart, + emitEnd, + emitPos + } = sourceMap; const comments = createCommentWriter(host, writer, sourceMap); - const { emitDetachedComments, emitLeadingComments, emitTrailingComments, getLeadingCommentsToEmit, getTrailingCommentsToEmit } = comments; + const { + getLeadingComments, + getLeadingCommentsOfPosition, + getTrailingComments, + getTrailingCommentsOfPosition, + emitLeadingComments, + emitTrailingComments, + emitDetachedComments + } = comments; let context: TransformationContext; let startLexicalEnvironment: () => void; @@ -233,14 +251,22 @@ const _super = (function (geti, seti) { } function emit(node: Node) { + emitWithWorker(node, emitWorker); + } + + function emitExpression(node: Expression) { + emitWithWorker(node, emitExpressionWorker); + } + + function emitWithWorker(node: Node, emitWorker: (node: Node) => void) { if (node) { const adviseOnEmit = isEmitNotificationEnabled(node); if (adviseOnEmit && onBeforeEmitNode) { onBeforeEmitNode(node); } - const leadingComments = getLeadingCommentsToEmit(node); - const trailingComments = getTrailingCommentsToEmit(node); + const leadingComments = getLeadingComments(node, getNotEmittedParent); + const trailingComments = getTrailingComments(node, getNotEmittedParent); emitLeadingComments(node, leadingComments); emitStart(node); emitWorker(node); @@ -253,7 +279,18 @@ const _super = (function (geti, seti) { } } - function emitWorker(node: Node) { + function getNotEmittedParent(node: Node): Node { + if (getNodeEmitFlags(node) & NodeEmitFlags.EmitCommentsOfNotEmittedParent) { + const parent = getOriginalNode(node).parent; + if (getNodeEmitFlags(parent) & NodeEmitFlags.IsNotEmittedNode) { + return parent; + } + } + + return undefined; + } + + function emitWorker(node: Node): void { const kind = node.kind; switch (kind) { // Pseudo-literals @@ -358,7 +395,7 @@ const _super = (function (geti, seti) { case SyntaxKind.ExpressionWithTypeArguments: return emitExpressionWithTypeArguments(node); case SyntaxKind.ThisType: - return write("this"); + return emitThisType(node); case SyntaxKind.StringLiteralType: return emitLiteral(node); @@ -374,7 +411,7 @@ const _super = (function (geti, seti) { case SyntaxKind.TemplateSpan: return emitTemplateSpan(node); case SyntaxKind.SemicolonClassElement: - return write(";"); + return emitSemicolonClassElement(node); // Statements case SyntaxKind.Block: @@ -382,7 +419,7 @@ const _super = (function (geti, seti) { case SyntaxKind.VariableStatement: return emitVariableStatement(node); case SyntaxKind.EmptyStatement: - return write(";"); + return emitEmptyStatement(node); case SyntaxKind.ExpressionStatement: return emitExpressionStatement(node); case SyntaxKind.IfStatement: @@ -501,23 +538,11 @@ const _super = (function (geti, seti) { // JSDoc nodes (ignored) } - if (isExpressionKind(kind)) { + if (isExpression(node)) { return emitExpressionWorker(node); } } - function emitExpression(node: Expression) { - if (node) { - const leadingComments = getLeadingCommentsToEmit(node); - const trailingComments = getTrailingCommentsToEmit(node); - emitLeadingComments(node, leadingComments); - emitStart(node); - emitExpressionWorker(node); - emitEnd(node); - emitTrailingComments(node, trailingComments); - } - } - function emitExpressionWorker(node: Node) { const kind = node.kind; if (isExpressionSubstitutionEnabled(node) && tryEmitSubstitute(node, expressionSubstitution)) { @@ -641,7 +666,7 @@ const _super = (function (geti, seti) { const text = temporaryVariables[nodeId] || (temporaryVariables[nodeId] = makeTempVariableName(tempKindToFlags(node.tempKind))); write(text); } - else if (nodeIsSynthesized(node)) { + else if (nodeIsSynthesized(node) || !node.parent) { if (getNodeEmitFlags(node) & NodeEmitFlags.UMDDefine) { writeLines(umdHelper); } @@ -741,13 +766,13 @@ const _super = (function (geti, seti) { emitModifiers(node, node.modifiers); writeIfPresent(node.asteriskToken, "*"); emit(node.name); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } function emitConstructor(node: ConstructorDeclaration) { emitModifiers(node, node.modifiers); write("constructor"); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } function emitAccessorDeclaration(node: AccessorDeclaration) { @@ -755,7 +780,7 @@ const _super = (function (geti, seti) { emitModifiers(node, node.modifiers); write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); emit(node.name); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } function emitCallSignature(node: CallSignatureDeclaration) { @@ -785,6 +810,10 @@ const _super = (function (geti, seti) { write(";"); } + function emitSemicolonClassElement(node: SemicolonClassElement) { + write(";"); + } + // // Types // @@ -851,6 +880,10 @@ const _super = (function (geti, seti) { write(")"); } + function emitThisType(node: ThisTypeNode) { + write("this"); + } + // // Binding patterns // @@ -896,7 +929,7 @@ const _super = (function (geti, seti) { write("[]"); } else { - const preferNewLine = getNodeEmitFlags(node) & NodeEmitFlags.MultiLine ? ListFormat.PreferNewLine : ListFormat.None; + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine); } } @@ -907,7 +940,7 @@ const _super = (function (geti, seti) { write("{}"); } else { - const preferNewLine = getNodeEmitFlags(node) & NodeEmitFlags.MultiLine ? ListFormat.PreferNewLine : ListFormat.None; + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None; emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); } @@ -998,27 +1031,8 @@ const _super = (function (geti, seti) { function emitArrowFunction(node: ArrowFunction) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); - const body = node.body; - if (isBlock(body)) { - const savedTempFlags = tempFlags; - tempFlags = 0; - startLexicalEnvironment(); - emitArrowFunctionHead(node); - write(" {"); - - const startingLine = writer.getLine(); - emitBlockFunctionBody(node, body); + emitSignatureAndBody(node, emitArrowFunctionHead); - const endingLine = writer.getLine(); - emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); - tempFlags = savedTempFlags; - write("}"); - } - else { - emitArrowFunctionHead(node); - write(" "); - emitExpression(body); - } } function emitArrowFunctionHead(node: ArrowFunction) { @@ -1186,6 +1200,10 @@ const _super = (function (geti, seti) { write(";"); } + function emitEmptyStatement(node: EmptyStatement) { + write(";"); + } + function emitExpressionStatement(node: ExpressionStatement) { emitExpression(node.expression); write(";"); @@ -1353,25 +1371,27 @@ const _super = (function (geti, seti) { emitModifiers(node, node.modifiers); write(node.asteriskToken ? "function* " : "function "); emit(node.name); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } - function emitSignatureAndBody(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) { + function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { const body = node.body; if (body) { - const savedTempFlags = tempFlags; - tempFlags = 0; - startLexicalEnvironment(); - emitSignatureHead(node); - write(" {"); - - const startingLine = writer.getLine(); - emitBlockFunctionBody(node, body); - - const endingLine = writer.getLine(); - emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); - write("}"); - tempFlags = savedTempFlags; + if (isBlock(body)) { + const savedTempFlags = tempFlags; + tempFlags = 0; + startLexicalEnvironment(); + emitSignatureHead(node); + write(" {"); + emitBlockFunctionBody(node, body); + write("}"); + tempFlags = savedTempFlags; + } + else { + emitSignatureHead(node); + write(" "); + emitExpression(body); + } } else { emitSignatureHead(node); @@ -1388,37 +1408,49 @@ const _super = (function (geti, seti) { function shouldEmitBlockFunctionBodyOnSingleLine(parentNode: Node, body: Block) { const originalNode = getOriginalNode(parentNode); - if (isFunctionLike(originalNode) && !nodeIsSynthesized(originalNode) && rangeEndIsOnSameLineAsRangeStart(originalNode.body, originalNode.body)) { - for (const statement of body.statements) { - if (synthesizedNodeStartsOnNewLine(statement)) { - return false; + if (isFunctionLike(originalNode) && !nodeIsSynthesized(originalNode)) { + const body = originalNode.body; + if (isBlock(body)) { + if (rangeEndIsOnSameLineAsRangeStart(body, body)) { + for (const statement of body.statements) { + if (synthesizedNodeStartsOnNewLine(statement)) { + return false; + } + } + + return true; } } - - if (originalNode.kind === SyntaxKind.ArrowFunction && !rangeEndIsOnSameLineAsRangeStart((originalNode).equalsGreaterThanToken, originalNode.body)) { - return false; + else { + return rangeEndIsOnSameLineAsRangeStart((originalNode).equalsGreaterThanToken, originalNode.body); } - - return true; } return false; } function emitBlockFunctionBody(parentNode: Node, body: Block) { - // Emit all the prologue directives (like "use strict"). + const startingLine = writer.getLine(); increaseIndent(); - const statements = body.statements; - const statementOffset = emitPrologueDirectives(statements, /*startWithNewLine*/ true); + emitDetachedComments(body.statements); + + // Emit all the prologue directives (like "use strict"). + const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true); const helpersEmitted = emitHelpers(body); - decreaseIndent(); if (statementOffset === 0 && !helpersEmitted && shouldEmitBlockFunctionBodyOnSingleLine(parentNode, body)) { - emitList(body, statements, ListFormat.SingleLineFunctionBodyStatements); + decreaseIndent(); + emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); + increaseIndent(); } else { - emitList(body, statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); + emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); } + + const endingLine = writer.getLine(); + emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); + emitLeadingComments(collapseTextRange(body.statements, TextRangeCollapse.CollapseToEnd)); + decreaseIndent(); } function emitClassDeclaration(node: ClassDeclaration) { @@ -1688,6 +1720,8 @@ const _super = (function (geti, seti) { write("case "); emitExpression(node.expression); write(":"); + + debugger; emitCaseOrDefaultClauseStatements(node, node.statements); } @@ -2051,8 +2085,10 @@ const _super = (function (geti, seti) { } else { // Write the opening line terminator or leading whitespace. + let shouldEmitInterveningComments = true; if (shouldWriteLeadingLineTerminator(parentNode, children, format)) { writeLine(); + shouldEmitInterveningComments = false; } else if (format & ListFormat.SpaceBetweenBraces) { write(" "); @@ -2076,12 +2112,20 @@ const _super = (function (geti, seti) { // Write either a line terminator or whitespace to separate the elements. if (shouldWriteSeparatingLineTerminator(previousSibling, child, format)) { writeLine(); + shouldEmitInterveningComments = false; } else if (previousSibling) { write(" "); } } + if (shouldEmitInterveningComments) { + emitLeadingComments(child, getTrailingCommentsOfPosition(child.pos)); + } + else { + shouldEmitInterveningComments = true; + } + // Emit this child. emit(child); @@ -2175,7 +2219,7 @@ const _super = (function (geti, seti) { return true; } else if (format & ListFormat.PreserveLines) { - if (getNodeEmitFlags(parentNode) & NodeEmitFlags.MultiLine) { + if (format & ListFormat.PreferNewLine) { return true; } @@ -2217,10 +2261,10 @@ const _super = (function (geti, seti) { function shouldWriteClosingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { if (format & ListFormat.MultiLine) { - return true; + return (format & ListFormat.NoTrailingNewLine) === 0; } else if (format & ListFormat.PreserveLines) { - if (getNodeEmitFlags(parentNode) & NodeEmitFlags.MultiLine) { + if (format & ListFormat.PreferNewLine) { return true; } @@ -2242,7 +2286,7 @@ const _super = (function (geti, seti) { function synthesizedNodeStartsOnNewLine(node: Node, format?: ListFormat) { if (nodeIsSynthesized(node)) { - const startsOnNewLine = (node).startsOnNewLine; + const startsOnNewLine = node.startsOnNewLine; if (startsOnNewLine === undefined) { return (format & ListFormat.PreferNewLine) !== 0; } @@ -2274,8 +2318,12 @@ const _super = (function (geti, seti) { } function needsIndentation(parent: Node, node1: Node, node2: Node): boolean { + parent = skipSynthesizedParentheses(parent); + node1 = skipSynthesizedParentheses(node1); + node2 = skipSynthesizedParentheses(node2); + // Always use a newline for synthesized code if the synthesizer desires it. - if (synthesizedNodeStartsOnNewLine(node2)) { + if (node2.startsOnNewLine) { return true; } @@ -2285,6 +2333,14 @@ const _super = (function (geti, seti) { && !rangeEndIsOnSameLineAsRangeStart(node1, node2); } + function skipSynthesizedParentheses(node: Node) { + while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) { + node = (node).expression; + } + + return node; + } + function getTextOfNode(node: Node, includeTrivia?: boolean) { if (nodeIsSynthesized(node) && (isLiteralExpression(node) || isIdentifier(node))) { return node.text; @@ -2304,9 +2360,9 @@ const _super = (function (geti, seti) { } function isSingleLineEmptyBlock(block: Block) { - return (getNodeEmitFlags(block) & NodeEmitFlags.MultiLine) === 0 && - block.statements.length === 0 && - rangeEndIsOnSameLineAsRangeStart(block, block); + return !block.multiLine + && block.statements.length === 0 + && rangeEndIsOnSameLineAsRangeStart(block, block); } function tempKindToFlags(kind: TempVariableKind) { @@ -2361,7 +2417,7 @@ const _super = (function (geti, seti) { function createBracketsMap() { const brackets: string[][] = []; brackets[ListFormat.Braces] = ["{", "}"]; - brackets[ListFormat.Parenthesis] = ["(", ")"]; + brackets[ListFormat.Parenthesis] = ["(",")"]; brackets[ListFormat.AngleBrackets] = ["<", ">"]; brackets[ListFormat.SquareBrackets] = ["[", "]"]; return brackets; @@ -2407,6 +2463,7 @@ const _super = (function (geti, seti) { // Other PreferNewLine = 1 << 15, // Prefer adding a LineTerminator between synthesized nodes. + NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list. // Precomputed Formats TypeLiteralMembers = MultiLine | Indented, @@ -2424,7 +2481,7 @@ const _super = (function (geti, seti) { MultiLineBlockStatements = Indented | MultiLine, VariableDeclarationList = CommaDelimited | SingleLine, SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenBraces, - MultiLineFunctionBodyStatements = MultiLine | Indented, + MultiLineFunctionBodyStatements = MultiLine, ClassHeritageClauses = SingleLine, ClassMembers = Indented | MultiLine, InterfaceMembers = Indented | MultiLine, @@ -2433,9 +2490,9 @@ const _super = (function (geti, seti) { NamedImportsOrExportsElements = CommaDelimited | AllowTrailingComma | SingleLine | SpaceBetweenBraces, JsxElementChildren = SingleLine, JsxElementAttributes = SingleLine, - CaseOrDefaultClauseStatements = Indented | MultiLine, + CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty, HeritageClauseTypes = CommaDelimited | SingleLine, - SourceFileStatements = MultiLine, + SourceFileStatements = MultiLine | NoTrailingNewLine, Decorators = MultiLine | Optional, TypeArguments = CommaDelimited | SingleLine | Indented | AngleBrackets | Optional, TypeParameters = CommaDelimited | SingleLine | Indented | AngleBrackets | Optional, diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4a3d0cbc164bf..089f53cf7c8e9 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -968,7 +968,7 @@ namespace ts { const start = new Date().getTime(); - const emitResult = emitFiles( + const emitResult = (options.experimentalTransforms ? printFiles : emitFiles)( emitResolver, getEmitHost(writeFileCallback), sourceFile); 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/sourcemap.ts b/src/compiler/sourcemap.ts index 5e453b1b10112..3866e4f956636 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -310,13 +310,13 @@ namespace ts { function emitStart(range: TextRange) { emitPos(getStartPos(range)); - if ((range).disableSourceMap) { + if (range.disableSourceMap) { disable(); } } function emitEnd(range: TextRange, stopOverridingEnd?: boolean) { - if ((range).disableSourceMap) { + if (range.disableSourceMap) { enable(); } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index b2c1007b7160f..be220f07ed8fb 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -84,8 +84,12 @@ namespace ts { hoistFunctionDeclaration, startLexicalEnvironment, endLexicalEnvironment, + identifierSubstitution: node => node, + expressionSubstitution: node => node, enableExpressionSubstitution, isExpressionSubstitutionEnabled, + onBeforeEmitNode: node => { }, + onAfterEmitNode: node => { }, enableEmitNotification, isEmitNotificationEnabled, }; @@ -148,8 +152,9 @@ namespace ts { /** * Sets flags that control emit behavior of a node. */ - function setNodeEmitFlags(node: Node, flags: NodeEmitFlags) { + function setNodeEmitFlags(node: T, flags: NodeEmitFlags) { nodeEmitFlags[getNodeId(node)] = flags; + return node; } /** diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index c21d81ac4d517..05b2350226b97 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -50,7 +50,7 @@ namespace ts { function emitAssignment(name: Identifier, value: Expression, location: TextRange) { const expression = createAssignment(name, value, location); if (isSimpleExpression(value)) { - (expression).disableSourceMap = true; + expression.disableSourceMap = true; } aggregateTransformFlags(expression); @@ -82,7 +82,7 @@ namespace ts { function emitAssignment(name: Identifier, value: Expression, location: TextRange) { const declaration = createVariableDeclaration(name, value, location); if (isSimpleExpression(value)) { - (declaration).disableSourceMap = true; + declaration.disableSourceMap = true; } aggregateTransformFlags(declaration); @@ -117,7 +117,7 @@ namespace ts { } if (isSimpleExpression(value)) { - (declaration).disableSourceMap = true; + declaration.disableSourceMap = true; } declaration.original = original; @@ -169,7 +169,7 @@ namespace ts { function emitPendingAssignment(name: Expression, value: Expression, location: TextRange, original: Node) { const expression = createAssignment(name, value, location); if (isSimpleExpression(value)) { - (expression).disableSourceMap = true; + expression.disableSourceMap = true; } expression.original = original; @@ -199,7 +199,7 @@ namespace ts { function emitDestructuringAssignment(bindingTarget: Expression | ShorthandPropertyAssignment, value: Expression, location: TextRange) { // When emitting target = value use source map node to highlight, including any temporary assignments needed for this let target: Expression; - if (isShortHandPropertyAssignment(bindingTarget)) { + if (isShorthandPropertyAssignment(bindingTarget)) { const initializer = visitor ? visitNode(bindingTarget.objectAssignmentInitializer, visitor, isExpression) : bindingTarget.objectAssignmentInitializer; diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index d5c5f9566db79..613ee5031f3e5 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -3,17 +3,74 @@ /*@internal*/ namespace ts { - // TODO(rbuckton): ES6->ES5 transformer export function transformES6(context: TransformationContext) { + const { + getGeneratedNameForNode, + makeUniqueName, + startLexicalEnvironment, + endLexicalEnvironment, + hoistVariableDeclaration, + setNodeEmitFlags, + enableExpressionSubstitution, + enableEmitNotification, + } = context; + + const resolver = context.getEmitResolver(); + const previousIdentifierSubstitution = context.identifierSubstitution; + const previousExpressionSubstitution = context.expressionSubstitution; + const previousOnBeforeEmitNode = context.onBeforeEmitNode; + const previousOnAfterEmitNode = context.onAfterEmitNode; + context.enableExpressionSubstitution(SyntaxKind.Identifier); + context.identifierSubstitution = substituteIdentifier; + context.expressionSubstitution = substituteExpression; + context.onBeforeEmitNode = onBeforeEmitNode; + context.onAfterEmitNode = onAfterEmitNode; + + let currentSourceFile: SourceFile; + let currentParent: Node; + let currentNode: Node; + let enclosingBlockScopeContainer: Node; + let enclosingBlockScopeContainerParent: Node; + let containingFunction: FunctionLikeDeclaration; + let containingNonArrowFunction: FunctionLikeDeclaration; + let combinedNodeFlags: NodeFlags; + + // This stack is is used to support substitutions when printing nodes. + let hasEnabledExpressionSubstitutionForCapturedThis = false; + let containingFunctionStack: FunctionLikeDeclaration[]; + return transformSourceFile; function transformSourceFile(node: SourceFile) { + currentSourceFile = node; return visitEachChild(node, visitor, context); } function visitor(node: Node): Node { + const savedContainingFunction = containingFunction; + const savedContainingNonArrowFunction = containingNonArrowFunction; + const savedCurrentParent = currentParent; + const savedCurrentNode = currentNode; + const savedEnclosingBlockScopeContainer = enclosingBlockScopeContainer; + const savedEnclosingBlockScopeContainerParent = enclosingBlockScopeContainerParent; + const savedCombinedNodeFlags = combinedNodeFlags; + + onBeforeVisitNode(node); + node = visitorWorker(node); + + containingFunction = savedContainingFunction; + containingNonArrowFunction = savedContainingNonArrowFunction; + currentParent = savedCurrentParent; + currentNode = savedCurrentNode; + enclosingBlockScopeContainer = savedEnclosingBlockScopeContainer; + enclosingBlockScopeContainerParent = savedEnclosingBlockScopeContainerParent; + combinedNodeFlags = savedCombinedNodeFlags; + return node; + } + + function visitorWorker(node: Node): Node { if (node.transformFlags & TransformFlags.ES6) { - return visitorWorker(node); + return visitJavaScript(node); } else if (node.transformFlags & TransformFlags.ContainsES6) { return visitEachChild(node, visitor, context); @@ -23,8 +80,1460 @@ namespace ts { } } - function visitorWorker(node: Node): Node { + function visitJavaScript(node: Node): Node { + 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.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.BinaryExpression: + return visitBinaryExpression(node); + + 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); + } + + Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + } + + function onBeforeVisitNode(node: Node) { + const currentGrandparent = currentParent; + currentParent = currentNode; + currentNode = node; + + combinedNodeFlags = combineNodeFlags(currentNode, currentParent, combinedNodeFlags); + + 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; + containingFunction = currentParent; + break; + + case SyntaxKind.ArrowFunction: + containingFunction = currentParent; + break; + } + } + } + + function visitClassDeclaration(node: ClassDeclaration): Statement { + return startOnNewLine( + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + getDeclarationName(node), + transformClassLikeDeclarationToExpression(node) + ) + ]), + node + ) + ); + } + + function visitClassExpression(node: ClassExpression): Expression { + return transformClassLikeDeclarationToExpression(node); + } + + function transformClassLikeDeclarationToExpression(node: ClassExpression | ClassDeclaration): Expression { + 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)] : [] + ) + ); + } + + function transformClassBody(node: ClassExpression | ClassDeclaration, hasExtendsClause: boolean): Block { + const statements: Statement[] = []; + startLexicalEnvironment(); + addExtendsHelperIfNeeded(statements, node, hasExtendsClause); + addConstructor(statements, node, hasExtendsClause); + addClassMembers(statements, node); + addLine(statements, createReturn(getDeclarationName(node))); + addLines(statements, endLexicalEnvironment()); + return createBlock(statements); + } + + function addExtendsHelperIfNeeded(classStatements: Statement[], node: ClassExpression | ClassDeclaration, hasExtendsClause: boolean): void { + if (hasExtendsClause) { + addLine(classStatements, + createStatement( + createExtendsHelper(getDeclarationName(node)) + ) + ); + } + } + + function addConstructor(classStatements: Statement[], node: ClassExpression | ClassDeclaration, hasExtendsClause: boolean): void { + const constructor = getFirstConstructorWithBody(node); + const hasSynthesizedSuper = hasSynthesizedDefaultSuperCall(constructor, hasExtendsClause); + addLine(classStatements, + createFunctionDeclaration( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + getDeclarationName(node), + transformConstructorParameters(constructor, hasSynthesizedSuper), + transformConstructorBody(constructor, hasExtendsClause, hasSynthesizedSuper), + /*location*/ constructor + ) + ); + } + + function transformConstructorParameters(constructor: ConstructorDeclaration, hasSynthesizedSuper: boolean): ParameterDeclaration[] { + if (constructor && !hasSynthesizedSuper) { + return visitNodes(constructor.parameters, visitor, isParameter); + } + + return []; + } + + function transformConstructorBody(constructor: ConstructorDeclaration, hasExtendsClause: boolean, hasSynthesizedSuper: boolean) { + const statements: Statement[] = []; + startLexicalEnvironment(); + if (constructor) { + addCaptureThisForNodeIfNeeded(statements, constructor); + addDefaultValueAssignments(statements, constructor); + addRestParameter(statements, constructor, hasSynthesizedSuper); + } + + addDefaultSuperCall(statements, constructor, hasExtendsClause, hasSynthesizedSuper); + + if (constructor) { + addNodes(statements, visitNodes(constructor.body.statements, visitor, isStatement, hasSynthesizedSuper ? 1 : 0)); + } + + addLines(statements, endLexicalEnvironment()); + return createBlock(statements, /*location*/ constructor && constructor.body); + } + + function addDefaultSuperCall(statements: Statement[], constructor: ConstructorDeclaration, hasExtendsClause: boolean, hasSynthesizedSuper: boolean) { + if (constructor ? hasSynthesizedSuper : hasExtendsClause) { + addLine(statements, + createStatement( + createFunctionApply( + createIdentifier("_super"), + createThis(), + createIdentifier("arguments") + ) + ) + ); + } + } + + 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; + } + } + + function shouldAddDefaultValueAssignments(node: FunctionLikeDeclaration): boolean { + return (node.transformFlags & TransformFlags.ContainsDefaultValueAssignments) !== 0; + } + + function addDefaultValueAssignments(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); + } + } + } + + 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) { + addLine(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList( + transformParameterBindingElements(parameter, temp) + ) + ) + ); + } + else if (initializer) { + addLine(statements, + createStatement( + createAssignment( + temp, + visitNode(initializer, visitor, isExpression) + ) + ) + ); + } + } + + function transformParameterBindingElements(parameter: ParameterDeclaration, name: Identifier) { + return flattenParameterDestructuring(parameter, name, visitor); + } + + function addDefaultValueAssignmentForInitializer(statements: Statement[], parameter: ParameterDeclaration, name: Identifier, initializer: Expression): void { + addLine(statements, + createIf( + createStrictEquality( + getSynthesizedNode(name), + createVoidZero() + ), + setNodeEmitFlags( + createBlock([ + createStatement( + createAssignment( + getSynthesizedNode(name), + visitNode(initializer, visitor, isExpression) + ) + ) + ]), + NodeEmitFlags.SingleLine + ) + ) + ); + } + + function shouldAddRestParameter(node: ParameterDeclaration) { + return node && node.dotDotDotToken; + } + + function addRestParameter(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper?: boolean): void { + if (inConstructorWithSynthesizedSuper) { + return; + } + + const parameter = lastOrUndefined(node.parameters); + if (!shouldAddRestParameter(parameter)) { + return; + } + + const name = getSynthesizedNode(parameter.name); + const restIndex = node.parameters.length - 1; + const temp = createLoopVariable(); + + // var param = []; + addLine(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + name, + createArrayLiteral([]) + ) + ]) + ) + ); + + // for (var _i = restIndex; _i < arguments.length; _i++) { + // param[_i - restIndex] = arguments[_i]; + // } + addLine(statements, + createFor( + createVariableDeclarationList([ + createVariableDeclaration(temp, createLiteral(restIndex)) + ]), + createLessThan( + temp, + createPropertyAccess(createIdentifier("arguments"), "length") + ), + createPostfixIncrement(temp), + createBlock([ + startOnNewLine( + createStatement( + createAssignment( + createElementAccess( + name, + restIndex === 0 ? temp : createSubtract(temp, createLiteral(restIndex)) + ), + createElementAccess(createIdentifier("arguments"), temp) + ) + ) + ) + ]) + ) + ); + } + + function addCaptureThisForNodeIfNeeded(statements: Statement[], node: Node): void { + if (node.transformFlags & TransformFlags.ContainsCapturedLexicalThis && node.kind !== SyntaxKind.ArrowFunction) { + enableExpressionSubstitutionForCapturedThis(); + + addLine(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + "_this", + createThis() + ) + ]) + ) + ); + } + } + + function addClassMembers(classStatements: Statement[], node: ClassExpression | ClassDeclaration): void { + for (const member of node.members) { + switch (member.kind) { + case SyntaxKind.SemicolonClassElement: + addLine(classStatements, transformSemicolonClassElementToStatement(member)); + break; + + case SyntaxKind.MethodDeclaration: + addLine(classStatements, transformClassMethodDeclarationToStatement(node, member)); + break; + + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + const accessors = getAllAccessorDeclarations(node.members, member); + if (member === accessors.firstAccessor) { + const receiver = getClassMemberPrefix(node, member); + addLine(classStatements, transformAccessorsToStatement(receiver, accessors)); + } + + break; + + case SyntaxKind.Constructor: + // Constructors are handled in visitClassExpression/visitClassDeclaration + break; + + default: + Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + break; + } + } + } + + function transformSemicolonClassElementToStatement(member: SemicolonClassElement) { + return createEmptyStatement(member); + } + + function transformClassMethodDeclarationToStatement(node: ClassExpression | ClassDeclaration, member: MethodDeclaration) { + const savedContainingFunction = containingFunction; + const savedContainingNonArrowFunction = containingNonArrowFunction; + containingFunction = containingNonArrowFunction = member; + const statement = createStatement( + createAssignment( + createMemberAccessForPropertyName( + getClassMemberPrefix(node, member), + visitNode(member.name, visitor, isPropertyName) + ), + transformFunctionLikeToExpression(member) + ), + /*location*/ member + ); + + containingFunction = savedContainingFunction; + containingNonArrowFunction = savedContainingNonArrowFunction; + return statement; + } + + function transformAccessorsToStatement(receiver: LeftHandSideExpression, accessors: AllAccessorDeclarations): Statement { + const savedContainingFunction = containingFunction; + const savedContainingNonArrowFunction = containingNonArrowFunction; + containingFunction = containingNonArrowFunction = accessors.firstAccessor; + const statement = createStatement( + transformAccessorsToExpression(receiver, accessors) + ); + containingFunction = savedContainingFunction; + containingNonArrowFunction = savedContainingNonArrowFunction; + return statement; + } + + 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), + set: setAccessor && transformFunctionLikeToExpression(setAccessor, /*location*/ setAccessor), + enumerable: true, + configurable: true + }, + /*preferNewLine*/ true, + /*location*/ firstAccessor + ); + } + + function transformFunctionLikeToExpression(node: FunctionLikeDeclaration, location?: TextRange, name?: Identifier): FunctionExpression { + return setOriginalNode( + createFunctionExpression( + /*asteriskToken*/ undefined, + name, + visitNodes(node.parameters, visitor, isParameter), + transformFunctionBody(node), + location + ), + node + ); + } + + function visitArrowFunction(node: ArrowFunction) { + if (node.transformFlags & TransformFlags.ContainsLexicalThis) { + enableExpressionSubstitutionForCapturedThis(); + } + + return transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined); + } + + function visitFunctionExpression(node: FunctionExpression): Expression { + return transformFunctionLikeToExpression(node, /*location*/ node, node.name); + } + + function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { + return setOriginalNode( + createFunctionDeclaration( + /*modifiers*/ undefined, + node.asteriskToken, // TODO(rbuckton): downlevel support for generators + node.name, + visitNodes(node.parameters, visitor, isParameter), + transformFunctionBody(node), + /*location*/ node + ), + node + ); + } + + function transformFunctionBody(node: FunctionLikeDeclaration) { + const statements: Statement[] = []; + startLexicalEnvironment(); + addCaptureThisForNodeIfNeeded(statements, node); + addDefaultValueAssignments(statements, node); + addRestParameter(statements, node); + + const body = node.body; + if (isBlock(body)) { + addNodes(statements, visitNodes(body.statements, visitor, isStatement)); + } + else { + const expression = visitNode(body, visitor, isExpression); + if (expression) { + addNode(statements, createReturn(expression, /*location*/ body)); + } + } + + addLines(statements, endLexicalEnvironment()); + return createBlock(statements, node.body); + } + + function visitBinaryExpression(node: BinaryExpression): Expression { + // If we are here it is because this is a destructuring assignment. + // TODO(rbuckton): Determine whether we need to save the value. + return flattenDestructuringAssignment(node, /*needsValue*/ true, hoistVariableDeclaration, visitor); + } + + function visitVariableDeclarationList(node: VariableDeclarationList): VariableDeclarationList { + return setOriginalNode( + createVariableDeclarationList( + flattenNodes(map(node.declarations, visitVariableDeclaration)), + /*location*/ node + ), + node + ); + } + + function visitVariableDeclaration(node: VariableDeclaration): OneOrMore { + const name = node.name; + if (isBindingPattern(name)) { + return createNodeArrayNode( + flattenVariableDestructuring(node, /*value*/ undefined, visitor) + ); + } + else { + let initializer = node.initializer; + // For binding pattern names that lack initializer 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 + if (!initializer) { + const original = getOriginalNode(node); + if (isVariableDeclaration(original)) { + // 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 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 emittedAsNestedLetDeclaration = combinedNodeFlags & NodeFlags.Let && !emittedAsTopLevel; + + const emitExplicitInitializer = + emittedAsNestedLetDeclaration + && enclosingBlockScopeContainer.kind !== SyntaxKind.ForInStatement + && enclosingBlockScopeContainer.kind !== SyntaxKind.ForOfStatement + && (!resolver.isDeclarationWithCollidingName(original) + || (isDeclaredInLoop + && !isCapturedInFunction + && !isIterationStatement(enclosingBlockScopeContainer, /*lookInLabeledStatements*/ false))); + + if (emitExplicitInitializer) { + initializer = createVoidZero(); + } + } + } + + return setOriginalNode( + createVariableDeclaration( + name, + visitNode(initializer, visitor, isExpression, /*optional*/ true), + /*location*/ node + ), + node + ); + } + } + + 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); + } + + 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 rhsIsIdentifier = expression.kind === SyntaxKind.Identifier; + const initializer = node.initializer; + const loopDeclarations: VariableDeclaration[] = []; + const loopBodyStatements: 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 + ? makeUniqueName((expression).text) + : createTempVariable(); + + // Initialize LHS + // var v = _a[_i]; + if (isVariableDeclarationList(initializer)) { + const declarations: VariableDeclaration[] = []; + 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. + addLine(loopBodyStatements, + 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. + addLine(loopBodyStatements, + 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. + addLine(loopBodyStatements, + createStatement( + flattenDestructuringAssignment( + assignment, + /*needsValue*/ false, + hoistVariableDeclaration, + visitor + ) + ) + ); + } + else { + addLine(loopBodyStatements, createStatement(assignment, /*location*/ node.initializer)); + } + } + + const statement = visitNode(node.statement, visitor, isStatement); + if (isBlock(statement)) { + addNodes(loopBodyStatements, statement.statements); + } + else { + addNode(loopBodyStatements, 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( + loopBodyStatements + ), + /*location*/ node + ); + } + + function shouldConvertLoopBody(node: IterationStatement): boolean { + return (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LoopWithCapturedBlockScopedBinding) !== 0; + } + + 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. + let initialProperties = visitNodes(properties, visitor, isObjectLiteralElement, 0, numInitialNonComputedProperties); + + const expressions: Expression[] = []; + addNode(expressions, + createAssignment( + temp, + setMultiLine( + createObjectLiteral( + visitNodes(properties, visitor, isObjectLiteralElement, 0, numInitialNonComputedProperties) + ), + 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, cloneNode(temp), node.multiLine); + return createParen(inlineExpressions(expressions)); + } + + function addObjectLiteralMembers(expressions: Expression[], node: ObjectLiteralExpression, receiver: Identifier, numInitialNonComputedProperties: number) { + const properties = node.properties; + for (let i = numInitialNonComputedProperties, len = properties.length; i < len; 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.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + break; + } + } + } + + function transformPropertyAssignmentToExpression(node: ObjectLiteralExpression, property: PropertyAssignment, receiver: Expression) { + return createAssignment( + createMemberAccessForPropertyName( + receiver, + visitNode(property.name, visitor, isPropertyName) + ), + visitNode(property.initializer, visitor, isExpression), + /*location*/ property + ); + } + + function transformShorthandPropertyAssignmentToExpression(node: ObjectLiteralExpression, property: ShorthandPropertyAssignment, receiver: Expression) { + return createAssignment( + createMemberAccessForPropertyName( + receiver, + visitNode(property.name, visitor, isPropertyName) + ), + getSynthesizedNode(property.name), + /*location*/ property + ); + } + + function transformObjectLiteralMethodDeclarationToExpression(node: ObjectLiteralExpression, method: MethodDeclaration, receiver: Expression) { + return createAssignment( + createMemberAccessForPropertyName( + receiver, + visitNode(method.name, visitor, isPropertyName) + ), + transformFunctionLikeToExpression(method, /*location*/ method), + /*location*/ method + ); + } + + 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(isIdentifier(node.name), `Unexpected node kind: ${formatSyntaxKind(node.name.kind)}.`); + return createPropertyAssignment( + node.name, + transformFunctionLikeToExpression(node, /*location*/ node), + /*location*/ node + ); + } + + function visitShorthandPropertyAssignment(node: ShorthandPropertyAssignment): ObjectLiteralElement { + return createPropertyAssignment( + node.name, + getSynthesizedNode(node.name), + /*location*/ node + ); + } + + function visitArrayLiteralExpression(node: ArrayLiteralExpression): Expression { + // We are here either because SuperKeyword was used somewhere in the expression, or + // because we contain a SpreadElementExpression. + if (node.transformFlags & TransformFlags.ContainsSpreadElementExpression) { + return transformAndSpreadElements(node.elements, /*needsUniqueCopy*/ true, node.multiLine); + } + else { + // We don't handle SuperKeyword here, so fall back. + return visitEachChild(node, visitor, context); + } + } + + 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 } = transformCallTarget(node.expression); + if (node.transformFlags & TransformFlags.ContainsSpreadElementExpression) { + return createFunctionApply( + target, + thisArg, + transformAndSpreadElements(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false) + ) + } + else { + Debug.assert(isSuperCall(node)); + return createFunctionCall( + target, + thisArg, + visitNodes(node.arguments, visitor, isExpression), + /*location*/ node + ); + } + } + + function visitNewExpression(node: NewExpression): LeftHandSideExpression { + // We are here either because we contain a SpreadElementExpression. + Debug.assert((node.transformFlags & TransformFlags.ContainsSpreadElementExpression) !== 0); + + // Transforms `new C(...a)` into `new ((_a = C).bind.apply(_a, [void 0].concat(a)))()`. + // Transforms `new x.C(...a)` into `new ((_a = x.C).bind.apply(_a, [void 0].concat(a)))()`. + const { target, thisArg } = transformCallTarget(createPropertyAccess(node.expression, "bind")); + return createNew( + createParen( + createFunctionApply( + target, + thisArg, + transformAndSpreadElements(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false, createVoidZero()) + ) + ), + [] + ); + } + + interface CallTarget { + target: Expression; + thisArg: Expression; + } + + function transformCallTarget(expression: Expression): CallTarget { + const callee = skipParentheses(expression); + switch (callee.kind) { + case SyntaxKind.PropertyAccessExpression: + return transformPropertyAccessCallTarget(callee); + + case SyntaxKind.ElementAccessExpression: + return transformElementAccessCallTarget(callee); + + case SyntaxKind.SuperKeyword: + return transformSuperCallTarget(callee); + + default: + const thisArg = createVoidZero(); + const target = visitNode(expression, visitor, isExpression); + return { target, thisArg }; + } + } + + function transformPropertyAccessCallTarget(node: PropertyAccessExpression): CallTarget { + if (node.expression.kind === SyntaxKind.SuperKeyword) { + // For `super.b()`, target is either `_super.b` (for static members) or + // `_super.prototype.b` (for instance members), and thisArg is `this`. + const thisArg = createThis(/*location*/ node.expression); + const target = createPropertyAccess( + visitSuperKeyword(node.expression), + node.name + ); + + return { target, thisArg }; + } + else { + // For `a.b()`, target is `(_a = a).b` and thisArg is `_a`. + const thisArg = createTempVariable(); + const target = createPropertyAccess( + createAssignment( + thisArg, + visitNode(node.expression, visitor, isExpression) + ), + node.name + ); + + return { target, thisArg }; + } + } + + function transformElementAccessCallTarget(node: ElementAccessExpression): CallTarget { + if (node.expression.kind === SyntaxKind.SuperKeyword) { + // For `super[b]()`, target is either `_super[b]` (for static members) or + // `_super.prototype[b]` (for instance members), and thisArg is `this`. + const thisArg = createThis(/*location*/ node.expression); + const target = createElementAccess( + visitSuperKeyword(node.expression), + visitNode(node.argumentExpression, visitor, isExpression) + ); + + return { target, thisArg }; + } + else { + // For `a[b]()`, expression is `(_a = a)[b]` and thisArg is `_a`. + const thisArg = createTempVariable(); + const target = createElementAccess( + createAssignment( + thisArg, + visitNode(node.expression, visitor, isExpression) + ), + visitNode(node.argumentExpression, visitor, isExpression) + ); + + return { target, thisArg }; + } + } + + function transformSuperCallTarget(node: PrimaryExpression): CallTarget { + // For `super()`, expression is `_super` and thisArg is `this`. + const thisArg = createThis(/*location*/ node); + const target = createIdentifier("_super"); + return { target, thisArg }; + } + + function transformAndSpreadElements(elements: NodeArray, needsUniqueCopy: boolean, multiLine: boolean, leadingExpression?: Expression): Expression { + const segments: Expression[] = []; + addNode(segments, leadingExpression); + + const length = elements.length; + let start = 0; + for (let i = 0; i < length; i++) { + const element = elements[i]; + if (isSpreadElementExpression(element)) { + if (i > start) { + addNode(segments, + setMultiLine( + createArrayLiteral( + visitNodes(elements, visitor, isExpression, start, i) + ), + multiLine + ) + ); + } + + addNode(segments, visitNode(element.expression, visitor, isExpression)); + start = i + 1; + } + } + + if (start < length) { + addNode(segments, + setMultiLine( + createArrayLiteral( + visitNodes(elements, visitor, isExpression, start, length) + ), + multiLine + ) + ); + } + + if (segments.length === 1) { + if (!leadingExpression && needsUniqueCopy && isSpreadElementExpression(elements[0])) { + return createArraySlice(segments[0]); + } + + return segments[0]; + } + + // Rewrite using the pattern .concat(, , ...) + return createArrayConcat(segments.shift(), segments); + } + + function visitTemplateLiteral(node: LiteralExpression): LeftHandSideExpression { + return createLiteral(node.text); + } + + function visitTaggedTemplateExpression(node: TaggedTemplateExpression): LeftHandSideExpression { + // 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)) { + addNode(cookedStrings, createLiteral(template.text)); + addNode(rawStrings, getRawLiteral(template)); + } + else { + addNode(cookedStrings, createLiteral(template.head.text)); + addNode(rawStrings, getRawLiteral(template.head)); + for (const templateSpan of template.templateSpans) { + addNode(cookedStrings, createLiteral(templateSpan.literal.text)); + addNode(rawStrings, getRawLiteral(templateSpan.literal)); + addNode(templateArguments, visitNode(templateSpan.expression, visitor, isExpression)); + } + } + + return createParen( + inlineExpressions([ + createAssignment(temp, createArrayLiteral(cookedStrings)), + createAssignment(createPropertyAccess(temp, "raw"), createArrayLiteral(rawStrings)), + createCall( + tag, + templateArguments + ) + ]) + ); + } + + 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 "${" + let 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); + } + + function visitTemplateExpression(node: TemplateExpression): Expression { + const expressions: Expression[] = []; + addTemplateHead(expressions, node); + addTemplateSpans(expressions, node.templateSpans); + + // 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; + } + + 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; + } + + function addTemplateHead(expressions: Expression[], node: TemplateExpression): void { + if (!shouldAddTemplateHead(node)) { + return; + } + + addNode(expressions, createLiteral(node.head.text)); + } + + function addTemplateSpans(expressions: Expression[], nodes: TemplateSpan[]): void { + for (const node of nodes) { + addNode(expressions, visitNode(node.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 (node.literal.text.length !== 0) { + addNode(expressions, createLiteral(node.literal.text)); + } + } + } + + function visitSuperKeyword(node: PrimaryExpression): LeftHandSideExpression { + const expression = createIdentifier("_super"); + return containingNonArrowFunction + && isClassElement(containingNonArrowFunction) + && (containingNonArrowFunction.flags & NodeFlags.Static) === 0 + ? createPropertyAccess(createIdentifier("_super"), "prototype") + : createIdentifier("_super"); + } + + function visitSourceFileNode(node: SourceFile): SourceFile { + const clone = cloneNode(node, node, node.flags, /*parent*/ undefined, node); + const statements: Statement[] = []; + startLexicalEnvironment(); + let statementOffset = addPrologueDirectives(statements, node.statements); + addCaptureThisForNodeIfNeeded(statements, node); + addNodes(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); + addNodes(statements, endLexicalEnvironment()); + clone.statements = createNodeArray(statements, node.statements); + return clone; + } + + function addPrologueDirectives(to: Statement[], from: NodeArray): number { + for (let i = 0; i < from.length; ++i) { + if (isPrologueDirective(from[i])) { + addNode(to, from[i]); + } + else { + return i; + } + } + + return from.length; + } + + var inEmit: boolean; + + function onBeforeEmitNode(node: Node) { + previousOnBeforeEmitNode(node); + + if (containingFunctionStack && isFunctionLike(node)) { + containingFunctionStack.push(node); + } + } + + function onAfterEmitNode(node: Node) { + previousOnAfterEmitNode(node); + + if (containingFunctionStack && isFunctionLike(node)) { + containingFunctionStack.pop(); + } + } + + function enableExpressionSubstitutionForCapturedThis() { + if (!hasEnabledExpressionSubstitutionForCapturedThis) { + hasEnabledExpressionSubstitutionForCapturedThis = true; + enableExpressionSubstitution(SyntaxKind.ThisKeyword); + enableEmitNotification(SyntaxKind.Constructor); + enableEmitNotification(SyntaxKind.MethodDeclaration); + enableEmitNotification(SyntaxKind.GetAccessor); + enableEmitNotification(SyntaxKind.SetAccessor); + enableEmitNotification(SyntaxKind.ArrowFunction); + enableEmitNotification(SyntaxKind.FunctionExpression); + enableEmitNotification(SyntaxKind.FunctionDeclaration); + containingFunctionStack = []; + } + } + + function substituteIdentifier(node: Identifier) { + node = previousIdentifierSubstitution(node); + + const original = getOriginalNode(node); + if (isIdentifier(original) && isNameOfDeclarationWithCollidingName(original)) { + return getGeneratedNameForNode(original); + } + return node; } + + function isNameOfDeclarationWithCollidingName(node: Identifier) { + const parent = node.parent; + if (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; + } + + + 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; + } + + function substituteExpressionIdentifier(node: Identifier): Identifier { + const original = getOriginalNode(node); + if (isIdentifier(original)) { + const declaration = resolver.getReferencedDeclarationWithCollidingName(original); + if (declaration) { + return getGeneratedNameForNode(declaration.name); + } + } + + return node; + } + + function substituteThisKeyword(node: PrimaryExpression): PrimaryExpression { + if (containingFunctionStack) { + const containingFunction = lastOrUndefined(containingFunctionStack); + if (containingFunction && getOriginalNode(containingFunction).kind === SyntaxKind.ArrowFunction) { + return createIdentifier("_this"); + } + } + + return node; + } + + function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) { + return node.name ? getSynthesizedNode(node.name) : getGeneratedNameForNode(node); + } + + function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) { + const expression = getDeclarationName(node); + return member.flags & NodeFlags.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 9dca322c17515..7be97566c51e8 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -30,7 +30,7 @@ namespace ts { return visitBinaryExpression(node); } - Debug.fail("Unexpected node kind."); + Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); } function visitBinaryExpression(node: BinaryExpression): Expression { @@ -90,7 +90,7 @@ namespace ts { return createMathPow(left, right, /*location*/ node); } else { - Debug.fail("Unexpected node kind."); + Debug.fail(`Unexpected operator kind: ${formatSyntaxKind(node.operatorToken.kind)}.`); } } } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 8ad85f153c9c4..fd8c568ac0e58 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -34,7 +34,7 @@ namespace ts { return visitJsxSelfClosingElement(node); } - Debug.fail("Unexpected node kind."); + Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); } function transformJsxChildToExpression(node: JsxChild): Expression { @@ -52,7 +52,7 @@ namespace ts { return visitJsxSelfClosingElement(node); } - Debug.fail("Unexpected node kind."); + Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); } function visitJsxElement(node: JsxElement) { @@ -85,14 +85,14 @@ namespace ts { properties = undefined; } - addNode(segments, transformJsxSpreadAttributeToExpression(attr), isExpression); + addNode(segments, transformJsxSpreadAttributeToExpression(attr)); } else { if (!properties) { properties = []; } - addNode(properties, transformJsxAttributeToObjectLiteralElement(attr), isObjectLiteralElement); + addNode(properties, transformJsxAttributeToObjectLiteralElement(attr)); } } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 616457e8b324f..f7e7e1287464f 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -31,14 +31,17 @@ namespace ts { context.onBeforeEmitNode = onBeforeEmitNode; context.onAfterEmitNode = onAfterEmitNode; - let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false; let currentSourceFile: SourceFile; let currentNamespace: ModuleDeclaration; let currentNamespaceLocalName: Identifier; let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; let currentParent: Node; let currentNode: Node; + let combinedNodeFlags: NodeFlags; let isRightmostExpression: boolean; + + // This stack is is used to support substitutions when printing nodes. + let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false; let superContainerStack: SuperContainer[]; return transformSourceFile; @@ -61,6 +64,7 @@ namespace ts { const savedCurrentScope = currentScope; const savedCurrentParent = currentParent; const savedCurrentNode = currentNode; + const savedCombinedNodeFlags = combinedNodeFlags; const savedIsRightmostExpression = isRightmostExpression; onBeforeVisitNode(node); node = visitor(node); @@ -68,6 +72,7 @@ namespace ts { currentScope = savedCurrentScope; currentParent = savedCurrentParent; currentNode = savedCurrentNode; + combinedNodeFlags = savedCombinedNodeFlags; isRightmostExpression = savedIsRightmostExpression; return node; } @@ -290,7 +295,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. @@ -313,7 +318,7 @@ namespace ts { return visitImportEqualsDeclaration(node); default: - Debug.fail("Unexpected node."); + Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); break; } } @@ -326,6 +331,9 @@ namespace ts { function onBeforeVisitNode(node: Node) { currentParent = currentNode; currentNode = node; + + combinedNodeFlags = combineNodeFlags(currentNode, currentParent, combinedNodeFlags); + switch (node.kind) { case SyntaxKind.SourceFile: case SyntaxKind.CaseBlock: @@ -357,7 +365,7 @@ namespace ts { const statements: Statement[] = []; const modifiers = visitNodes(node.modifiers, visitor, isModifier); const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); - const members = transformClassMembers(node, heritageClauses !== undefined); + const members = transformClassMembers(node, firstOrUndefined(heritageClauses) !== undefined); let decoratedClassAlias: Identifier; // emit name if @@ -744,7 +752,10 @@ namespace ts { // End the lexical environment. addNodes(statements, endLexicalEnvironment()); - return createBlock(statements); + return setMultiLine( + createBlock(statements, constructor ? constructor.body : undefined), + true + ); } /** @@ -855,7 +866,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; } /** @@ -868,8 +889,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; } @@ -880,12 +902,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 ); } @@ -1467,7 +1490,7 @@ namespace ts { break; default: - Debug.fail("Cannot serialize unexpected type node."); + Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); break; } @@ -2053,7 +2076,8 @@ namespace ts { return createStatement( inlineExpressions( map(variables, transformInitializedVariable) - ) + ), + /*location*/ node ); } @@ -2113,7 +2137,10 @@ namespace ts { location = undefined; } - const namespaceMemberName = getNamespaceMemberName(node.name); + const name = isNamespaceExport(node) + ? getNamespaceMemberName(node.name) + : getSynthesizedNode(node.name); + currentNamespaceLocalName = getGeneratedNameForNode(node); addNode(statements, createStatement( @@ -2127,9 +2154,9 @@ namespace ts { ) ), [createLogicalOr( - namespaceMemberName, + name, createAssignment( - namespaceMemberName, + name, createObjectLiteral() ) )] @@ -2143,7 +2170,7 @@ namespace ts { createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList([ - createVariableDeclaration(node.name, namespaceMemberName) + createVariableDeclaration(node.name, name) ]), location ) @@ -2229,12 +2256,20 @@ namespace ts { * @param node The await expression node. */ function visitAwaitExpression(node: AwaitExpression): Expression { - const expression = createYield( - visitNode(node.expression, visitor, isExpression), + const expression = setOriginalNode( + createYield( + visitNode(node.expression, visitor, isExpression), + node + ), node ); - return isRightmostExpression ? expression : createParen(expression); + return isRightmostExpression + ? expression + : setOriginalNode( + createParen(expression, /*location*/ node), + node + ); } /** @@ -2265,11 +2300,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 = cloneNode(child, child, child.flags, child.parent, original); + } + + setNodeEmitFlags(parent, NodeEmitFlags.IsNotEmittedNode); + setNodeEmitFlags(child, NodeEmitFlags.EmitCommentsOfNotEmittedParent); + return child; } /** @@ -2305,10 +2358,14 @@ namespace ts { location = undefined; } + const name = isNamespaceExport(node) + ? getNamespaceMemberName(node.name) + : getSynthesizedNode(node.name); + let moduleParam: Expression = createLogicalOr( - getNamespaceMemberName(node.name), + name, createAssignment( - getNamespaceMemberName(node.name), + name, createObjectLiteral([]) ) ); @@ -2515,13 +2572,10 @@ namespace ts { } function getNamespaceMemberName(name: Identifier): Expression { - name = getSynthesizedNode(name); - return currentNamespaceLocalName - ? createPropertyAccess(currentNamespaceLocalName, name) - : name + return createPropertyAccess(currentNamespaceLocalName, getSynthesizedNode(name)); } - function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) { + function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration | EnumDeclaration) { return node.name ? getSynthesizedNode(node.name) : getGeneratedNameForNode(node); } @@ -2536,7 +2590,7 @@ namespace ts { } function substituteExpression(node: Expression): Expression { - node = previousExpressionSubstitution ? previousExpressionSubstitution(node) : node; + node = previousExpressionSubstitution(node); switch (node.kind) { case SyntaxKind.Identifier: return substituteExpressionIdentifier(node); @@ -2556,19 +2610,30 @@ namespace ts { return node; } - function substituteExpressionIdentifier(node: Identifier) { - if (!nodeIsSynthesized(node) && resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) { - // 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 original = getOriginalNode(node); - const declaration = resolver.getReferencedValueDeclaration(isIdentifier(original) ? original : node); - if (declaration) { - const classAlias = currentDecoratedClassAliases[getNodeId(declaration)]; - if (classAlias) { - return cloneNode(classAlias); + function substituteExpressionIdentifier(node: Identifier): Expression { + const original = getOriginalNode(node); + if (isIdentifier(original)) { + if (resolver.getNodeCheckFlags(original) & NodeCheckFlags.BodyScopedClassBinding) { + // 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 cloneNode(classAlias); + } } } + + const container = resolver.getReferencedExportContainer(original); + if (container && container.kind === SyntaxKind.ModuleDeclaration) { + return createPropertyAccess( + getGeneratedNameForNode(container), + cloneNode(node), + /*location*/ node + ); + } } return node; @@ -2652,6 +2717,8 @@ namespace ts { } function onBeforeEmitNode(node: Node): void { + previousOnAfterEmitNode(node); + const kind = node.kind; if (kind === SyntaxKind.ClassDeclaration && node.decorators) { currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)]; @@ -2673,6 +2740,8 @@ namespace ts { } function onAfterEmitNode(node: Node): void { + previousOnAfterEmitNode(node); + const kind = node.kind; if (kind === SyntaxKind.ClassDeclaration && node.decorators) { currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 847b00a1c9765..47908d322a21e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -20,6 +20,7 @@ namespace ts { export interface TextRange { pos: number; end: number; + /* @internal */ disableSourceMap?: boolean; // Whether a synthesized text range disables source maps for its contents (used by transforms). } // token > SyntaxKind.Identifer => token is a keyword @@ -449,6 +450,7 @@ namespace ts { /* @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. + /* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms). /* @internal */ jsDocComment?: JSDocComment; // JSDoc for the node, if it has any. Only for .js files. /* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding) /* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding) @@ -1124,6 +1126,7 @@ namespace ts { // @kind(SyntaxKind.Block) export interface Block extends Statement { statements: NodeArray; + multiLine?: boolean; } // @kind(SyntaxKind.VariableStatement) @@ -2466,6 +2469,7 @@ namespace ts { allowSyntheticDefaultImports?: boolean; allowJs?: boolean; /* @internal */ stripInternal?: boolean; + /* @internal */ experimentalTransforms?: boolean; // Skip checking lib.d.ts to help speed up tests. /* @internal */ skipDefaultLibCheck?: boolean; @@ -2767,6 +2771,7 @@ namespace ts { ContainsParameterPropertyAssignments = 1 << 13, ContainsSpreadElementExpression = 1 << 14, ContainsComputedPropertyName = 1 << 15, + ContainsBlockScopedBinding = 1 << 16, // Assertions // - Bitmasks that are used to assert facts about the syntax of a node and its subtree. @@ -2779,12 +2784,12 @@ namespace ts { // - Bitmasks that exclude flags from propagating out of a specific context // into the subtree flags of their container. NodeExcludes = TypeScript | Jsx | ES7 | ES6, - ArrowFunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments, - FunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments, - ConstructorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsParameterPropertyAssignments, - MethodOrAccessorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis, + ArrowFunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding, + FunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding, + ConstructorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding, + MethodOrAccessorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding, ClassExcludes = ContainsDecorators | ContainsPropertyInitializer | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsParameterPropertyAssignments, - ModuleExcludes = ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis, + ModuleExcludes = ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding, TypeExcludes = ~ContainsTypeScript, ObjectLiteralExcludes = ContainsDecorators | ContainsComputedPropertyName, ArrayLiteralOrCallOrNewExcludes = ContainsSpreadElementExpression, @@ -2792,15 +2797,16 @@ namespace ts { /* @internal */ export const enum NodeEmitFlags { - EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node. - EmitExportStar = 1 << 1, // The export * helper should be written to this node. - EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods. - EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods. - UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. - NoLexicalEnvironment = 1 << 5, // A new LexicalEnvironment should *not* be introduced when emitting this node, this is primarily used when printing a SystemJS module. - SingleLine = 1 << 6, // The contents of this node should be emit on a single line. - MultiLine = 1 << 7, // The contents of this node should be emit on multiple lines. - AdviseOnEmitNode = 1 << 8, // The node printer should invoke the onBeforeEmitNode and onAfterEmitNode callbacks when printing this node. + EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node. + EmitExportStar = 1 << 1, // The export * helper should be written to this node. + EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods. + EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods. + UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. + NoLexicalEnvironment = 1 << 5, // A new LexicalEnvironment should *not* be introduced when emitting this node, this is primarily used when printing a SystemJS module. + SingleLine = 1 << 6, // The contents of this node should be emit on a single line. + 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 << 8, // Emits comments of missing parent nodes. } /** Additional context provided to `visitEachChild` */ @@ -2817,7 +2823,7 @@ namespace ts { getCompilerOptions(): CompilerOptions; getEmitResolver(): EmitResolver; getNodeEmitFlags(node: Node): NodeEmitFlags; - setNodeEmitFlags(node: Node, flags: NodeEmitFlags): void; + setNodeEmitFlags(node: T, flags: NodeEmitFlags): T; hoistFunctionDeclaration(node: FunctionDeclaration): void; hoistVariableDeclaration(node: Identifier): void; isUniqueName(name: string): boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b84feea4153fa..aa7d0affab633 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -8,13 +8,6 @@ namespace ts { isNoDefaultLib?: boolean; } - export interface SynthesizedNode extends Node { - leadingCommentRanges?: CommentRange[]; - trailingCommentRanges?: CommentRange[]; - startsOnNewLine?: boolean; - disableSourceMap?: boolean; - } - export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration { const declarations = symbol.declarations; if (declarations) { @@ -330,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 { @@ -354,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; } @@ -488,6 +491,19 @@ namespace ts { return node; } + /** + * Combines the flags of a node with the combined flags of its parent if they can be combined. + */ + export function combineNodeFlags(node: Node, parentNode: Node, previousNodeFlags: NodeFlags) { + if ((node.kind === SyntaxKind.VariableDeclarationList && parentNode.kind === SyntaxKind.VariableStatement) || + (node.kind === SyntaxKind.VariableDeclaration && parentNode.kind === SyntaxKind.VariableDeclarationList) || + (node.kind === SyntaxKind.BindingElement)) { + return node.flags | previousNodeFlags; + } + + return node.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 @@ -1733,7 +1749,7 @@ namespace ts { return getOperatorPrecedence(expression.kind, operator, hasArguments); } - function getOperator(expression: Expression) { + export function getOperator(expression: Expression) { if (expression.kind === SyntaxKind.BinaryExpression) { return (expression).operatorToken.kind; } @@ -2318,26 +2334,33 @@ namespace ts { } } - export function emitComments(text: string, lineMap: number[], writer: EmitTextWriter, comments: CommentRange[], trailingSeparator: boolean, newLine: string, + export function emitComments(text: string, lineMap: number[], writer: EmitTextWriter, comments: CommentRange[], leadingSeparator: boolean, trailingSeparator: boolean, newLine: string, writeComment: (text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) => void) { - let emitLeadingSpace = !trailingSeparator; - forEach(comments, comment => { - if (emitLeadingSpace) { + if (comments && comments.length > 0) { + if (leadingSeparator) { writer.write(" "); - emitLeadingSpace = false; } - writeComment(text, lineMap, writer, comment, newLine); - if (comment.hasTrailingNewLine) { - writer.writeLine(); + + let emitInterveningSeperator = false; + for (const comment of comments) { + if (emitInterveningSeperator) { + writer.write(" "); + emitInterveningSeperator = false; + } + + writeComment(text, lineMap, writer, comment, newLine); + if (comment.hasTrailingNewLine) { + writer.writeLine(); + } + else { + emitInterveningSeperator = true; + } } - else if (trailingSeparator) { + + if (emitInterveningSeperator && trailingSeparator) { writer.write(" "); } - else { - // Emit leading space to separate comment during next comment emit - emitLeadingSpace = true; - } - }); + } } /** @@ -2394,7 +2417,7 @@ namespace ts { if (nodeLine >= lastCommentLine + 2) { // Valid detachedComments emitNewLineBeforeLeadingComments(lineMap, writer, node, leadingComments); - emitComments(text, lineMap, writer, detachedComments, /*trailingSeparator*/ true, newLine, writeComment); + emitComments(text, lineMap, writer, detachedComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: lastOrUndefined(detachedComments).end }; } } @@ -2795,6 +2818,35 @@ 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.end } + : { pos: range.end, end: range.end }; + } + // Node tests // // All node tests in the following list should *not* reference parent pointers so that @@ -2812,6 +2864,10 @@ namespace ts { // Literals + export function isNoSubstitutionTemplateLiteral(node: Node): node is LiteralExpression { + return node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; + } + export function isLiteralKind(kind: SyntaxKind): boolean { return SyntaxKind.FirstLiteralToken <= kind && kind <= SyntaxKind.LastLiteralToken; } @@ -2905,6 +2961,10 @@ namespace ts { // Type members + export function isMethodDeclaration(node: Node): node is MethodDeclaration { + return node.kind === SyntaxKind.MethodDeclaration; + } + export function isClassElement(node: Node): node is ClassElement { const kind = node.kind; return kind === SyntaxKind.Constructor @@ -2991,11 +3051,16 @@ namespace ts { || kind === SyntaxKind.NoSubstitutionTemplateLiteral; } + export function isSpreadElementExpression(node: Node): node is SpreadElementExpression { + return node.kind === SyntaxKind.SpreadElementExpression; + } + export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { return node.kind === SyntaxKind.ExpressionWithTypeArguments; } - function isLeftHandSideExpressionKind(kind: SyntaxKind) { + export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { + const kind = node.kind; return kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression || kind === SyntaxKind.NewExpression @@ -3021,11 +3086,8 @@ namespace ts { || kind === SyntaxKind.SuperKeyword; } - export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { - return isLeftHandSideExpressionKind(node.kind); - } - - function isUnaryExpressionKind(kind: SyntaxKind): boolean { + export function isUnaryExpression(node: Node): node is UnaryExpression { + const kind = node.kind; return kind === SyntaxKind.PrefixUnaryExpression || kind === SyntaxKind.PostfixUnaryExpression || kind === SyntaxKind.DeleteExpression @@ -3033,14 +3095,11 @@ namespace ts { || kind === SyntaxKind.VoidExpression || kind === SyntaxKind.AwaitExpression || kind === SyntaxKind.TypeAssertionExpression - || isLeftHandSideExpressionKind(kind); + || isLeftHandSideExpression(node); } - export function isUnaryExpression(node: Node): node is UnaryExpression { - return isUnaryExpressionKind(node.kind); - } - - export function isExpressionKind(kind: SyntaxKind): boolean { + export function isExpression(node: Node): node is Expression { + const kind = node.kind; return kind === SyntaxKind.ConditionalExpression || kind === SyntaxKind.YieldExpression || kind === SyntaxKind.ArrowFunction @@ -3048,11 +3107,7 @@ namespace ts { || kind === SyntaxKind.SpreadElementExpression || kind === SyntaxKind.AsExpression || kind === SyntaxKind.OmittedExpression - || isUnaryExpressionKind(kind); - } - - export function isExpression(node: Node): node is Expression { - return isExpressionKind(node.kind); + || isUnaryExpression(node); } // Misc @@ -3264,9 +3319,12 @@ namespace ts { // Property assignments - export function isShortHandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { - const kind = node.kind; - return kind === SyntaxKind.ShorthandPropertyAssignment; + export function isPropertyAssignment(node: Node): node is PropertyAssignment { + return node.kind === SyntaxKind.PropertyAssignment; + } + + export function isShorthandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { + return node.kind === SyntaxKind.ShorthandPropertyAssignment; } // Enum diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 0ddcc770a9f32..3292881e01475 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -19,6 +19,8 @@ namespace ts { /** A callback used to lift a NodeArrayNode into a valid node. */ lift?: (nodes: NodeArray) => Node; + + parenthesize?: (value: Node, parentNode: Node) => Node; }; /** @@ -52,7 +54,7 @@ namespace ts { { name: "modifiers", test: isModifier }, { name: "name", test: isBindingName }, { name: "type", test: isTypeNode, optional: true }, - { name: "initializer", test: isExpression, optional: true }, + { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.Decorator]: [ { name: "expression", test: isLeftHandSideExpression }, @@ -108,34 +110,34 @@ namespace ts { [SyntaxKind.BindingElement]: [ { name: "propertyName", test: isPropertyName, optional: true }, { name: "name", test: isBindingName }, - { name: "initializer", test: isExpression, optional: true }, + { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.ArrayLiteralExpression]: [ - { name: "elements", test: isExpression }, + { name: "elements", test: isExpression, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.ObjectLiteralExpression]: [ { name: "properties", test: isObjectLiteralElement }, ], [SyntaxKind.PropertyAccessExpression]: [ - { name: "expression", test: isLeftHandSideExpression }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "name", test: isIdentifier }, ], [SyntaxKind.ElementAccessExpression]: [ - { name: "expression", test: isLeftHandSideExpression }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "argumentExpression", test: isExpression }, ], [SyntaxKind.CallExpression]: [ - { name: "expression", test: isLeftHandSideExpression }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "typeArguments", test: isTypeNode }, { name: "arguments", test: isExpression }, ], [SyntaxKind.NewExpression]: [ - { name: "expression", test: isLeftHandSideExpression }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "typeArguments", test: isTypeNode }, { name: "arguments", test: isExpression }, ], [SyntaxKind.TaggedTemplateExpression]: [ - { name: "tag", test: isLeftHandSideExpression }, + { name: "tag", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "template", test: isTemplate }, ], [SyntaxKind.TypeAssertionExpression]: [ @@ -163,26 +165,26 @@ namespace ts { { name: "body", test: isConciseBody, lift: liftToBlock }, ], [SyntaxKind.DeleteExpression]: [ - { name: "expression", test: isUnaryExpression }, + { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand }, ], [SyntaxKind.TypeOfExpression]: [ - { name: "expression", test: isUnaryExpression }, + { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand }, ], [SyntaxKind.VoidExpression]: [ - { name: "expression", test: isUnaryExpression }, + { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand }, ], [SyntaxKind.AwaitExpression]: [ - { name: "expression", test: isUnaryExpression }, + { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand }, ], [SyntaxKind.PrefixUnaryExpression]: [ - { name: "operand", test: isUnaryExpression }, + { name: "operand", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand }, ], [SyntaxKind.PostfixUnaryExpression]: [ - { name: "operand", test: isLeftHandSideExpression }, + { name: "operand", test: isLeftHandSideExpression, parenthesize: parenthesizePostfixOperand }, ], [SyntaxKind.BinaryExpression]: [ - { name: "left", test: isExpression }, - { name: "right", test: isExpression }, + { name: "left", test: isExpression, parenthesize: (node: Expression, parent: BinaryExpression) => parenthesizeBinaryOperand(getOperator(parent), node, true) }, + { name: "right", test: isExpression, parenthesize: (node: Expression, parent: BinaryExpression) => parenthesizeBinaryOperand(getOperator(parent), node, false) }, ], [SyntaxKind.ConditionalExpression]: [ { name: "condition", test: isExpression }, @@ -197,7 +199,7 @@ namespace ts { { name: "expression", test: isExpression, optional: true }, ], [SyntaxKind.SpreadElementExpression]: [ - { name: "expression", test: isExpression }, + { name: "expression", test: isExpression, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.ClassExpression]: [ { name: "decorators", test: isDecorator }, @@ -208,7 +210,7 @@ namespace ts { { name: "members", test: isClassElement }, ], [SyntaxKind.ExpressionWithTypeArguments]: [ - { name: "expression", test: isLeftHandSideExpression }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "typeArguments", test: isTypeNode }, ], [SyntaxKind.AsExpression]: [ @@ -228,7 +230,7 @@ namespace ts { { name: "declarationList", test: isVariableDeclarationList }, ], [SyntaxKind.ExpressionStatement]: [ - { name: "expression", test: isExpression }, + { name: "expression", test: isExpression, parenthesize: parenthesizeExpressionForExpressionStatement }, ], [SyntaxKind.IfStatement]: [ { name: "expression", test: isExpression }, @@ -291,7 +293,7 @@ namespace ts { [SyntaxKind.VariableDeclaration]: [ { name: "name", test: isBindingName }, { name: "type", test: isTypeNode, optional: true }, - { name: "initializer", test: isExpression, optional: true }, + { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.VariableDeclarationList]: [ { name: "declarations", test: isVariableDeclaration }, @@ -420,7 +422,7 @@ namespace ts { ], [SyntaxKind.PropertyAssignment]: [ { name: "name", test: isPropertyName }, - { name: "initializer", test: isExpression }, + { name: "initializer", test: isExpression, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.ShorthandPropertyAssignment]: [ { name: "name", test: isIdentifier }, @@ -428,7 +430,7 @@ namespace ts { ], [SyntaxKind.EnumMember]: [ { name: "name", test: isPropertyName }, - { name: "initializer", test: isExpression, optional: true }, + { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.SourceFile]: [ { name: "statements", test: isStatement }, @@ -492,7 +494,6 @@ namespace ts { Debug.assert(test === undefined || test(visited), "Wrong node type after visit."); aggregateTransformFlags(visited); - visited.original = node; return visited; } @@ -541,14 +542,14 @@ namespace ts { aggregateTransformFlags(visited); } - addNode(updated, visited, test); + addNodeWorker(updated, visited, /*addOnNewLine*/ undefined, test); } } if (updated !== undefined) { return (isModifiersArray(nodes) ? createModifiersArray(updated, nodes) - : createNodeArray(updated, nodes)); + : setHasTrailingComma(createNodeArray(updated, nodes), nodes.hasTrailingComma)); } return nodes; @@ -577,21 +578,27 @@ 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) { const visited = visitEdge(edge, value, visitor); + if (visited && isArray(visited) && isModifiersArray(visited)) { + modifiers = visited.flags; + } + if (updated !== undefined || visited !== value) { if (updated === undefined) { updated = cloneNode(node, /*location*/ node, node.flags & ~NodeFlags.Modifier, /*parent*/ undefined, /*original*/ node); } - if (visited && isArray(visited) && isModifiersArray(visited)) { - updated[edge.name] = visited; - updated.flags |= visited.flags; + if (modifiers) { + updated.flags |= modifiers; + modifiers = undefined; } - else { - updated[edge.name] = visited; + + if (visited !== value) { + setEdgeValue(updated, edge, visited); } } } @@ -617,6 +624,17 @@ namespace ts { return updated; } + /** + * Sets the value of an edge, adjusting the value as necessary for cases such as expression precedence. + */ + function setEdgeValue(parentNode: Node & Map, edge: NodeEdge, value: Node | NodeArray) { + if (value && edge.parenthesize && !isArray(value)) { + value = parenthesizeEdge(value, parentNode, edge.parenthesize, edge.test); + } + + parentNode[edge.name] = value; + } + /** * Visits a node edge. * @@ -627,7 +645,31 @@ namespace ts { function visitEdge(edge: NodeEdge, value: Node | NodeArray, visitor: (node: Node) => Node) { return isArray(value) ? visitNodes(>value, visitor, edge.test, /*start*/ undefined, /*count*/ undefined) - : visitNode(value, visitor, edge.test, edge.optional, edge.lift); + : visitNode(value, visitor, !edge.parenthesize ? edge.test : undefined, edge.optional, edge.lift); + } + + /** + * Applies parentheses to a node to ensure the correct precedence. + */ + function parenthesizeEdge(node: Node, parentNode: Node, parenthesize: (node: Node, parentNode: Node) => Node, test: (node: Node) => boolean) { + node = parenthesize(node, parentNode); + Debug.assert(test === undefined || test(node), "Unexpected node kind after visit."); + return node; + } + + /** + * Flattens an array of nodes that could contain NodeArrayNodes. + */ + export function flattenNodes(nodes: OneOrMore[]): T[] { + let result: T[]; + if (nodes) { + result = []; + for (const node of nodes) { + addNode(result, node); + } + } + + return result; } /** @@ -635,31 +677,61 @@ 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: OneOrMore, test?: (node: Node) => boolean) { + export function addNode(to: T[], from: OneOrMore, startOnNewLine?: boolean) { + addNodeWorker(to, from, startOnNewLine, /*test*/ undefined) + } + + /** + * Appends an array of nodes to an array. + * + * @param to The destination NodeArray. + * @param from The source array of Node or NodeArrayNode. + */ + export function addNodes(to: T[], from: OneOrMore[], startOnNewLine?: boolean) { + addNodesWorker(to, from, startOnNewLine, /*test*/ undefined); + } + + /** + * Appends a node to an array on a new line. + * + * @param to The destination array. + * @param from The source Node or NodeArrayNode. + */ + export function addLine(to: T[], from: OneOrMore) { + addNodeWorker(to, from, /*addOnNewLine*/ true, /*test*/ undefined); + } + + /** + * Appends an array of nodes to an array on new lines. + * + * @param to The destination NodeArray. + * @param from The source array of Node or NodeArrayNode. + */ + export function addLines(to: T[], from: OneOrMore[]) { + addNodesWorker(to, from, /*addOnNewLine*/ true, /*test*/ undefined); + } + + function addNodeWorker(to: T[], from: OneOrMore, addOnNewLine: boolean, test: (node: Node) => boolean) { if (to && from) { if (isNodeArrayNode(from)) { - addNodes(to, from.nodes, test); + addNodesWorker(to, from.nodes, addOnNewLine, test); } else { Debug.assert(test === undefined || test(from), "Wrong node type after visit."); + if (addOnNewLine) { + startOnNewLine(from); + } + to.push(from); } } } - /** - * Appends an array of nodes to an array. - * - * @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: OneOrMore[], test?: (node: Node) => boolean) { + function addNodesWorker(to: T[], from: OneOrMore[], addOnNewLine: boolean, test: (node: Node) => boolean) { if (to && from) { for (const node of from) { - addNode(to, node, test); + addNodeWorker(to, node, addOnNewLine, test); } } } From 39628d7cffcad81f2f2530f9596adeb300905d43 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 19 Feb 2016 18:23:23 -0800 Subject: [PATCH 09/43] Added CommonJS/AMD/UMD module transformer --- src/compiler/transformers/es6.ts | 19 +- src/compiler/transformers/module/es6.ts | 4 - src/compiler/transformers/module/module.ts | 696 ++++++++++++++++++++- src/compiler/transformers/ts.ts | 12 +- src/compiler/utilities.ts | 84 +++ src/compiler/visitor.ts | 18 +- 6 files changed, 790 insertions(+), 43 deletions(-) diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 613ee5031f3e5..84f58308e13a7 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -659,7 +659,7 @@ namespace ts { ); } - function visitVariableDeclaration(node: VariableDeclaration): OneOrMore { + function visitVariableDeclaration(node: VariableDeclaration): OneOrMany { const name = node.name; if (isBindingPattern(name)) { return createNodeArrayNode( @@ -1375,7 +1375,7 @@ namespace ts { const clone = cloneNode(node, node, node.flags, /*parent*/ undefined, node); const statements: Statement[] = []; startLexicalEnvironment(); - let statementOffset = addPrologueDirectives(statements, node.statements); + let statementOffset = copyPrologueDirectives(node.statements, statements); addCaptureThisForNodeIfNeeded(statements, node); addNodes(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); addNodes(statements, endLexicalEnvironment()); @@ -1383,21 +1383,6 @@ namespace ts { return clone; } - function addPrologueDirectives(to: Statement[], from: NodeArray): number { - for (let i = 0; i < from.length; ++i) { - if (isPrologueDirective(from[i])) { - addNode(to, from[i]); - } - else { - return i; - } - } - - return from.length; - } - - var inEmit: boolean; - function onBeforeEmitNode(node: Node) { previousOnBeforeEmitNode(node); 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..c515a50ec8d63 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -5,14 +5,704 @@ namespace ts { // TODO(rbuckton): CommonJS/AMD/UMD transformer export function transformModule(context: TransformationContext) { + const transformModuleDelegates: Map<(node: SourceFile) => Statement[]> = { + [ModuleKind.None]: transformCommonJSModule, + [ModuleKind.CommonJS]: transformCommonJSModule, + [ModuleKind.AMD]: transformAMDModule, + [ModuleKind.UMD]: transformUMDModule, + }; + + const { + getGeneratedNameForNode, + 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; 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)); + + const moduleTransformer = transformModuleDelegates[moduleKind]; + const statements = moduleTransformer(node); + const updated = cloneNode(node, node, node.flags, /*parent*/ undefined, node); + updated.statements = createNodeArray(statements, node.statements); + + if (hasExportStars && moduleTransformer === transformCommonJSModule) { + setNodeEmitFlags(updated, NodeEmitFlags.EmitExportStar); + } + + currentSourceFile = undefined; + externalImports = undefined; + exportSpecifiers = undefined; + exportEquals = undefined; + hasExportStars = false; + return updated; + } + + return node; + } + + function transformCommonJSModule(node: SourceFile) { + const statements: Statement[] = []; + startLexicalEnvironment(); + + const statementOffset = copyPrologueDirectives(node.statements, statements); + addNodes(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); + addNodes(statements, endLexicalEnvironment()); + addNode(statements, tryCreateExportEquals(/*emitAsReturn*/ false)); + + return statements; + } + + function transformAMDModule(node: SourceFile) { + const define = createIdentifier("define"); + const moduleName = node.moduleName ? createLiteral(node.moduleName) : undefined; + return transformAsynchronousModule(node, define, moduleName, /*includeNonAmdDependencies*/ true); + } + + function transformUMDModule(node: SourceFile) { + const define = createIdentifier("define"); + setNodeEmitFlags(define, NodeEmitFlags.UMDDefine); + return transformAsynchronousModule(node, define, /*moduleName*/ undefined, /*includeNonAmdDependencies*/ false); + } + + function transformAsynchronousModule(node: SourceFile, define: Expression, moduleName: Expression, includeNonAmdDependencies: boolean) { + const statements: Statement[] = []; + startLexicalEnvironment(); + + const statementOffset = copyPrologueDirectives(node.statements, statements); + + // An AMD define function has the following shape: + // define(id?, dependencies?, factory); + // + // This has the shape of + // 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 defineArguments: Expression[] = []; + 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); + } + } + + // Add the module name. + addNode(defineArguments, moduleName); + + // Create the import names array. + addNode(defineArguments, createArrayLiteral(concatenate(aliasedModuleNames, unaliasedModuleNames))); + + // Create the body of the module. + const moduleBodyStatements: Statement[] = []; + + // Start the lexical environment for the module body. + startLexicalEnvironment(); + + // Pipe each statement of the source file through a visitor and out to the module body + addNodes(moduleBodyStatements, visitNodes(node.statements, visitor, isStatement, statementOffset)); + + // End the lexical environment for the module body. + addNodes(moduleBodyStatements, endLexicalEnvironment()); + + // Append the 'export =' statement if provided. + addNode(moduleBodyStatements, tryCreateExportEquals(/*emitAsReturn*/ true)); + + // Create the function for the module body. + const moduleBody = setMultiLine(createBlock(moduleBodyStatements), true); + + if (hasExportStars) { + setNodeEmitFlags(moduleBody, NodeEmitFlags.EmitExportStar); + } + + addNode(defineArguments, + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + importAliasNames, + moduleBody + ) + ); + + addNode(statements, + createStatement( + createCall( + define, + defineArguments + ) + ) + ); + + return statements; + } + + function visitor(node: Node) { + 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: + return node; + } + } + + function visitImportDeclaration(node: ImportDeclaration): OneOrMany { + if (contains(externalImports, node)) { + 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( + getSynthesizedNode(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( + getSynthesizedNode(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( + getSynthesizedNode(namespaceDeclaration.name), + getGeneratedNameForNode(node), + /*location*/ node + ) + ]) + ) + ); + } + + addExportImportAssignments(statements, node); + return createNodeArrayNode(statements); + } + + return undefined; + } + + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMany { + if (contains(externalImports, node)) { + const statements: Statement[] = []; + if (moduleKind !== ModuleKind.AMD) { + if (node.flags & NodeFlags.Export) { + addNode(statements, + createStatement( + createExportAssignment( + node.name, + createRequireCall(node) + ), + /*location*/ node + ) + ); + } + else { + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + getSynthesizedNode(node.name), + createRequireCall(node), + /*location*/ node + ) + ]) + ) + ); + } + } + else { + if (node.flags & NodeFlags.Export) { + addNode(statements, + createStatement( + createExportAssignment(node.name, node.name), + /*location*/ node + ) + ); + } + } + + addExportImportAssignments(statements, node); + return createNodeArrayNode(statements); + } + + return undefined; + } + + function visitExportDeclaration(node: ExportDeclaration): OneOrMany { + if (contains(externalImports, node)) { + let 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 createNodeArrayNode(statements); + } + else { + // export * from "mod"; + return createStatement( + createCall( + createIdentifier("__export"), + [ + moduleKind !== ModuleKind.AMD + ? createRequireCall(node) + : generatedName + ] + ), + /*location*/ node + ); + } + } + + return undefined; + } + + function visitExportAssignment(node: ExportAssignment): OneOrMany { + if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { + const statements: Statement[] = []; + addExportDefault(statements, node.expression, /*location*/ node); + return createNodeArrayNode(statements); + } + + return undefined; + } + + 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): OneOrMany { + 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 getModuleMemberName(name: Identifier) { + return createPropertyAccess( + createIdentifier("exports"), + name, + /*location*/ name + ); + } + + function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMany { + const statements: Statement[] = []; + if (node.name) { + if (node.flags & NodeFlags.Export) { + addNode(statements, + createFunctionDeclaration( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + node.name, + node.parameters, + node.body, + /*location*/ node + ) + ); + + if (node.flags & NodeFlags.Default) { + addExportDefault(statements, getGeneratedNameForNode(node.name), /*location*/ node); + } + } + else { + addNode(statements, node); + } + + addExportMemberAssignments(statements, node.name); + } + else { + Debug.assert((node.flags & NodeFlags.Default) !== 0); + addExportDefault(statements, + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + node.parameters, + node.body, + /*location*/ node + ), + /*location*/ node + ); + } + return createNodeArrayNode(statements); + } + + function visitClassDeclaration(node: ClassDeclaration): OneOrMany { + const statements: Statement[] = []; + if (node.name) { + if (node.flags & NodeFlags.Export) { + addNode(statements, + createClassDeclaration( + /*modifiers*/ undefined, + node.name, + node.heritageClauses, + node.members, + /*location*/ node + ) + ); + + if (node.flags & NodeFlags.Default) { + addExportDefault(statements, getSynthesizedNode(node.name), /*location*/ node); + } + } + else { + addNode(statements, node); + } + + addExportMemberAssignments(statements, node.name); + } + else { + Debug.assert((node.flags & NodeFlags.Default) !== 0); + addExportDefault(statements, + createClassExpression( + /*name*/ undefined, + node.heritageClauses, + node.members, + /*location*/ node + ), + /*location*/ node + ); + } + + return createNodeArrayNode(statements); + } + + function substituteExpression(node: Expression) { + node = previousExpressionSubstitution(node); + if (isIdentifier(node)) { + return substituteExpressionIdentifier(node); + } + + return node; } - function visitor(node: Node): Node { + function substituteExpressionIdentifier(node: Identifier): Expression { + const container = resolver.getReferencedExportContainer(node); + if (container && container.kind === SyntaxKind.SourceFile) { + return createPropertyAccess( + createIdentifier("exports"), + getSynthesizedNode(node), + /*location*/ node + ); + } + return node; } + + 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; + } + + function getExternalModuleNameLiteral(importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration) { + const moduleName = getExternalModuleName(importNode); + if (moduleName.kind === SyntaxKind.StringLiteral) { + return tryRenameExternalModule(moduleName) + || getSynthesizedNode(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 { + let 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 getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + if (node.kind === SyntaxKind.ImportEqualsDeclaration) { + return node; + } + + let importClause = (node).importClause; + if (importClause && importClause.namedBindings && importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { + return importClause.namedBindings; + } + } + + function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + return node.kind === SyntaxKind.ImportDeclaration + && (node).importClause + && !!(node).importClause.name; + } + + function createRequireCall(importNode: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + const moduleName = getExternalModuleNameLiteral(importNode); + return createCall( + createIdentifier("require"), + moduleName ? [moduleName] : [] + ); + } + + function createExportAssignment(name: Identifier, value: Expression) { + let exports = createIdentifier("exports"); + let exportMember: LeftHandSideExpression; + if (name.originalKeywordKind && languageVersion === ScriptTarget.ES3) { + let exportName = createLiteral(name.text); + exportMember = createElementAccess(exports, exportName); + } + else { + let exportName = getSynthesizedNode(name); + exportMember = createPropertyAccess(exports, exportName); + } + + return createAssignment(exportMember, value); + } } -} \ No newline at end of file +} diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index f7e7e1287464f..736b0406bedbe 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1795,7 +1795,7 @@ namespace ts { * * @param node The function node. */ - function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMore { + function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMany { if (shouldElideFunctionLikeDeclaration(node)) { return undefined; } @@ -2081,14 +2081,6 @@ namespace ts { ); } - function getInitializedVariables(node: VariableDeclarationList) { - return filter(node.declarations, isInitializedVariable); - } - - function isInitializedVariable(node: VariableDeclaration) { - return node.initializer !== undefined; - } - function transformInitializedVariable(node: VariableDeclaration): Expression { const name = node.name; if (isBindingPattern(name)) { @@ -2446,7 +2438,7 @@ namespace ts { * * @param node The import equals declaration node. */ - function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMore { + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMany { Debug.assert(!isExternalModuleImportEqualsDeclaration(node)); if (shouldElideImportEqualsDeclaration(node)) { return undefined; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index aa7d0affab633..aa3c860a9815e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2847,6 +2847,90 @@ namespace ts { : { 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 (let 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 diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 3292881e01475..8f5cb736dbf0f 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -2,7 +2,7 @@ /* @internal */ namespace ts { - export type OneOrMore = T | NodeArrayNode; + export type OneOrMany = T | NodeArrayNode; /** * Describes an edge of a Node, used when traversing a syntax tree. @@ -531,7 +531,7 @@ namespace ts { // Visit each original node. for (let i = 0; i < count; i++) { const node = nodes[i + start]; - const visited = node && >visitor(node); + const visited = node && >visitor(node); if (updated !== undefined || visited === undefined || visited !== node) { if (updated === undefined) { // Ensure we have a copy of `nodes`, up to the current index. @@ -660,7 +660,7 @@ namespace ts { /** * Flattens an array of nodes that could contain NodeArrayNodes. */ - export function flattenNodes(nodes: OneOrMore[]): T[] { + export function flattenNodes(nodes: OneOrMany[]): T[] { let result: T[]; if (nodes) { result = []; @@ -678,7 +678,7 @@ namespace ts { * @param to The destination array. * @param from The source Node or NodeArrayNode. */ - export function addNode(to: T[], from: OneOrMore, startOnNewLine?: boolean) { + export function addNode(to: T[], from: OneOrMany, startOnNewLine?: boolean) { addNodeWorker(to, from, startOnNewLine, /*test*/ undefined) } @@ -688,7 +688,7 @@ namespace ts { * @param to The destination NodeArray. * @param from The source array of Node or NodeArrayNode. */ - export function addNodes(to: T[], from: OneOrMore[], startOnNewLine?: boolean) { + export function addNodes(to: T[], from: OneOrMany[], startOnNewLine?: boolean) { addNodesWorker(to, from, startOnNewLine, /*test*/ undefined); } @@ -698,7 +698,7 @@ namespace ts { * @param to The destination array. * @param from The source Node or NodeArrayNode. */ - export function addLine(to: T[], from: OneOrMore) { + export function addLine(to: T[], from: OneOrMany) { addNodeWorker(to, from, /*addOnNewLine*/ true, /*test*/ undefined); } @@ -708,11 +708,11 @@ namespace ts { * @param to The destination NodeArray. * @param from The source array of Node or NodeArrayNode. */ - export function addLines(to: T[], from: OneOrMore[]) { + export function addLines(to: T[], from: OneOrMany[]) { addNodesWorker(to, from, /*addOnNewLine*/ true, /*test*/ undefined); } - function addNodeWorker(to: T[], from: OneOrMore, addOnNewLine: boolean, test: (node: Node) => boolean) { + function addNodeWorker(to: T[], from: OneOrMany, addOnNewLine: boolean, test: (node: Node) => boolean) { if (to && from) { if (isNodeArrayNode(from)) { addNodesWorker(to, from.nodes, addOnNewLine, test); @@ -728,7 +728,7 @@ namespace ts { } } - function addNodesWorker(to: T[], from: OneOrMore[], addOnNewLine: boolean, test: (node: Node) => boolean) { + function addNodesWorker(to: T[], from: OneOrMany[], addOnNewLine: boolean, test: (node: Node) => boolean) { if (to && from) { for (const node of from) { addNodeWorker(to, node, addOnNewLine, test); From 5564537a0a3f35329e4c64b4a6d42d0a3950ac74 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 14:41:10 -0800 Subject: [PATCH 10/43] Added initial SystemJS transformer. --- src/compiler/factory.ts | 33 + src/compiler/transformers/module/module.ts | 335 ++--- src/compiler/transformers/module/system.ts | 1279 +++++++++++++++++++- src/compiler/utilities.ts | 17 + src/compiler/visitor.ts | 27 +- 5 files changed, 1537 insertions(+), 154 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 5fdffc32d46de..04f99e66fe5b6 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -145,6 +145,13 @@ namespace ts { return clone; } + /** + * Creates a shallow, memberwise clone of a node for mutation. + */ + export function getMutableNode(node: T): T { + return cloneNode(node, node, node.flags, node.parent, node); + } + export function createNodeArrayNode(elements: T[]): NodeArrayNode { const node = >createSynthesizedNode(SyntaxKind.NodeArrayNode); node.nodes = createNodeArray(elements); @@ -364,6 +371,13 @@ namespace ts { return node; } + export function createPrefix(operator: SyntaxKind, operand: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.PrefixUnaryExpression, location); + node.operator = operator; + node.operand = parenthesizePrefixOperand(operand); + return node; + } + export function createPostfix(operand: Expression, operator: SyntaxKind, location?: TextRange) { const node = createNode(SyntaxKind.PostfixUnaryExpression, location); node.operand = parenthesizePostfixOperand(operand); @@ -512,6 +526,14 @@ namespace ts { return node; } + export function createForOf(initializer: ForInitializer, expression: Expression, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.ForOfStatement, location); + node.initializer = initializer; + node.expression = expression; + node.statement = statement; + return node; + } + export function createReturn(expression?: Expression, location?: TextRange): ReturnStatement { const node = createNode(SyntaxKind.ReturnStatement, location); node.expression = expression; @@ -630,6 +652,10 @@ namespace ts { return createBinary(left, SyntaxKind.BarBarToken, right); } + export function createLogicalNot(operand: Expression) { + return createPrefix(SyntaxKind.ExclamationToken, operand); + } + export function createVoidZero() { return createVoid(createLiteral(0)); } @@ -790,6 +816,13 @@ namespace ts { ); } + export function createHasOwnProperty(target: LeftHandSideExpression, propertyName: Expression) { + return createCall( + createPropertyAccess(target, "hasOwnProperty"), + [propertyName] + ); + } + function createPropertyDescriptor({ get, set, value, enumerable, configurable, writable }: PropertyDescriptorOptions, preferNewLine?: boolean, location?: TextRange) { const properties: ObjectLiteralElement[] = []; addPropertyAssignment(properties, "get", get, preferNewLine); diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index c515a50ec8d63..5ef6a0d182566 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -3,9 +3,8 @@ /*@internal*/ namespace ts { - // TODO(rbuckton): CommonJS/AMD/UMD transformer export function transformModule(context: TransformationContext) { - const transformModuleDelegates: Map<(node: SourceFile) => Statement[]> = { + const transformModuleDelegates: Map<(node: SourceFile) => SourceFile> = { [ModuleKind.None]: transformCommonJSModule, [ModuleKind.CommonJS]: transformCommonJSModule, [ModuleKind.AMD]: transformAMDModule, @@ -36,21 +35,20 @@ namespace ts { return transformSourceFile; + /** + * Transforms the module aspects of a SourceFile. + * + * @param node The SourceFile node. + */ function transformSourceFile(node: SourceFile) { if (isExternalModule(node) || compilerOptions.isolatedModules) { currentSourceFile = node; - // collect information about the external module + // Collect information about the external module. ({ externalImports, exportSpecifiers, exportEquals, hasExportStars } = collectExternalModuleInfo(node, resolver)); - const moduleTransformer = transformModuleDelegates[moduleKind]; - const statements = moduleTransformer(node); - const updated = cloneNode(node, node, node.flags, /*parent*/ undefined, node); - updated.statements = createNodeArray(statements, node.statements); - - if (hasExportStars && moduleTransformer === transformCommonJSModule) { - setNodeEmitFlags(updated, NodeEmitFlags.EmitExportStar); - } + // Perform the transformation. + const updated = transformModuleDelegates[moduleKind](node); currentSourceFile = undefined; externalImports = undefined; @@ -63,147 +61,154 @@ namespace ts { return node; } + /** + * Transforms a SourceFile into a CommonJS module. + * + * @param node The SourceFile node. + */ function transformCommonJSModule(node: SourceFile) { - const statements: Statement[] = []; startLexicalEnvironment(); - - const statementOffset = copyPrologueDirectives(node.statements, statements); - addNodes(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); - addNodes(statements, endLexicalEnvironment()); - addNode(statements, tryCreateExportEquals(/*emitAsReturn*/ false)); - - return statements; + 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) { - const statements: Statement[] = []; - startLexicalEnvironment(); - - const statementOffset = copyPrologueDirectives(node.statements, statements); - - // An AMD define function has the following shape: - // define(id?, dependencies?, factory); - // - // This has the shape of - // 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 defineArguments: Expression[] = []; - 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); - } - } - - // Add the module name. - addNode(defineArguments, moduleName); - - // Create the import names array. - addNode(defineArguments, createArrayLiteral(concatenate(aliasedModuleNames, unaliasedModuleNames))); - - // Create the body of the module. - const moduleBodyStatements: Statement[] = []; - // Start the lexical environment for the module body. startLexicalEnvironment(); - // Pipe each statement of the source file through a visitor and out to the module body - addNodes(moduleBodyStatements, visitNodes(node.statements, visitor, isStatement, statementOffset)); - - // End the lexical environment for the module body. - addNodes(moduleBodyStatements, endLexicalEnvironment()); - - // Append the 'export =' statement if provided. - addNode(moduleBodyStatements, tryCreateExportEquals(/*emitAsReturn*/ true)); - - // Create the function for the module body. - const moduleBody = setMultiLine(createBlock(moduleBodyStatements), true); - - if (hasExportStars) { - setNodeEmitFlags(moduleBody, NodeEmitFlags.EmitExportStar); - } + const { importModuleNames, importAliasNames } = collectAsynchronousDependencies(node, includeNonAmdDependencies); - addNode(defineArguments, - createFunctionExpression( - /*asteriskToken*/ undefined, - /*name*/ undefined, - importAliasNames, - moduleBody - ) - ); - - addNode(statements, + // Create an updated SourceFile: + // + // define(moduleName?, ["module1", "module2"], function ... + return updateSourceFile(node, [ createStatement( createCall( define, - defineArguments + flattenNodes([ + // 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( + flattenNodes([ + // 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 + ) + ) + ]) ) ) - ); - - return statements; + ]); } + /** + * Visits a node at the top level of the source file. + * + * @param node The node. + */ function visitor(node: Node) { 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): OneOrMany { if (contains(externalImports, node)) { const statements: Statement[] = []; @@ -428,9 +433,7 @@ namespace ts { createObjectDefineProperty( createIdentifier("exports"), createLiteral("_esModule"), - { - value: createLiteral(true) - } + { value: createLiteral(true) } ) ); } @@ -500,14 +503,6 @@ namespace ts { } } - function getModuleMemberName(name: Identifier) { - return createPropertyAccess( - createIdentifier("exports"), - name, - /*location*/ name - ); - } - function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMany { const statements: Statement[] = []; if (node.name) { @@ -524,7 +519,7 @@ namespace ts { ); if (node.flags & NodeFlags.Default) { - addExportDefault(statements, getGeneratedNameForNode(node.name), /*location*/ node); + addExportDefault(statements, getSynthesizedNode(node.name), /*location*/ node); } } else { @@ -631,6 +626,14 @@ namespace ts { return undefined; } + 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) { @@ -653,7 +656,7 @@ namespace ts { } function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): Identifier { - let namespaceDeclaration = getNamespaceDeclarationNode(node); + const namespaceDeclaration = getNamespaceDeclarationNode(node); if (namespaceDeclaration && !isDefaultImport(node)) { return createIdentifier(getSourceTextOfNodeFromSourceFile(currentSourceFile, namespaceDeclaration.name)); } @@ -665,23 +668,6 @@ namespace ts { } } - function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { - if (node.kind === SyntaxKind.ImportEqualsDeclaration) { - return node; - } - - let importClause = (node).importClause; - if (importClause && importClause.namedBindings && importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { - return importClause.namedBindings; - } - } - - function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { - return node.kind === SyntaxKind.ImportDeclaration - && (node).importClause - && !!(node).importClause.name; - } - function createRequireCall(importNode: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { const moduleName = getExternalModuleNameLiteral(importNode); return createCall( @@ -691,18 +677,83 @@ namespace ts { } function createExportAssignment(name: Identifier, value: Expression) { - let exports = createIdentifier("exports"); - let exportMember: LeftHandSideExpression; - if (name.originalKeywordKind && languageVersion === ScriptTarget.ES3) { - let exportName = createLiteral(name.text); - exportMember = createElementAccess(exports, exportName); + return createAssignment( + name.originalKeywordKind && languageVersion === ScriptTarget.ES3 + ? createElementAccess( + createIdentifier("exports"), + createLiteral(name.text) + ) + : createPropertyAccess( + createIdentifier("exports"), + getSynthesizedNode(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)); + } } - else { - let exportName = getSynthesizedNode(name); - exportMember = createPropertyAccess(exports, exportName); + + 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 createAssignment(exportMember, value); + return { + importModuleNames: [ + ...unaliasedModuleNames, + ...aliasedModuleNames + ], + importAliasNames + }; + } + + function updateSourceFile(node: SourceFile, statements: Statement[]) { + const updated = cloneNode(node, node, node.flags, /*parent*/ undefined, node); + updated.statements = createNodeArray(statements, node.statements); + return updated; } } } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index e4c03d32b45d8..9ffc613ae0c86 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -3,16 +3,1289 @@ /*@internal*/ namespace ts { - // TODO(rbuckton): System module transformer export function transformSystemModule(context: TransformationContext) { + interface DependencyGroup { + name: Identifier; + externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; + } + + const { + getGeneratedNameForNode, + makeUniqueName, + 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[]; + let noSubstitution: Map = {}; + 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 = makeUniqueName("exports"); + contextObjectForFile = makeUniqueName("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 = makeUniqueName("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 = makeUniqueName(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): Node { + 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): Node { + 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): OneOrMany { + if (!node.moduleSpecifier) { + const statements: Statement[] = []; + addNodes(statements, map(node.exportClause.elements, visitExportSpecifier)); + return createNodeArrayNode(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): OneOrMany { + const isExported = (node.flags & NodeFlags.Export) !== 0; + 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 (node.flags & NodeFlags.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 ((node.flags & NodeFlags.Default) === 0) { + 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): OneOrMany { + // 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 (node.flags & NodeFlags.Export) { + if ((node.flags & NodeFlags.Default) === 0) { + recordExportName(name); + } + + addNode(statements, createDeclarationExport(node)); + } + + return createNodeArrayNode(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 = getMutableNode(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 = getMutableNode(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 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 = getMutableNode(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 = getMutableNode(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 = getMutableNode(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 = getMutableNode(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 = getMutableNode(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 = getMutableNode(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 = getMutableNode(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 = getMutableNode(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 visitor(node: Node): Node { + function substituteBinaryExpression(node: BinaryExpression): Expression { + if (isAssignmentOperator(node.operatorToken.kind)) { + return substituteAssignmentExpression(node); + } + return node; } + + function substituteAssignmentExpression(node: BinaryExpression): Expression { + if (!noSubstitution[getNodeId(node)]) { + noSubstitution[getNodeId(node)] = true; + 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)) { + let expression = node.expression; + return isIdentifier(expression) && isExportedBinding(expression); + } + else { + return hasExportedReferenceInDestructuringElement(node); + } + } + + function hasExportedReferenceInDestructuringElement(node: Expression): boolean { + if (isBinaryExpression(node)) { + let left = node.left; + return node.operatorToken.kind === SyntaxKind.EqualsToken + && isDestructuringPattern(left) + && hasExportedReferenceInDestructuringPattern(left); + } + else if (isIdentifier(node)) { + return isExportedBinding(node); + } + else if (isSpreadElementExpression(node)) { + let 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) { + let moduleName = getExternalModuleName(importNode); + if (moduleName.kind === SyntaxKind.StringLiteral) { + return tryRenameExternalModule(moduleName) + || getSynthesizedNode(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 { + let name = getLocalNameForExternalImport(node); + return name ? name.text : undefined; + } + + function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): Identifier { + let 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 ? getSynthesizedNode(node.name) : getGeneratedNameForNode(node); + } + + function addExportStarFunction(statements: Statement[], localNames: Identifier) { + const exportStarFunction = makeUniqueName("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) { + let declarationName = getDeclarationName(node); + let exportName = node.flags & NodeFlags.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, getSynthesizedNode(name)); + } + } + + function collectDependencyGroups(externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]) { + let groupIndices: Map = {}; + let dependencyGroups: DependencyGroup[] = []; + for (let i = 0; i < externalImports.length; ++i) { + let externalImport = externalImports[i]; + let externalModuleName = getExternalModuleNameLiteral(externalImport); + let text = externalModuleName.text; + if (hasProperty(groupIndices, text)) { + // deduplicate/group entries in dependency list by the dependency name + let 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 hoistExportedVariableDeclaration(name: Identifier) { + + } + + function hoistExportedFunctionDeclaration(node: FunctionDeclaration) { + + } + + function recordExportedFunctionDeclaration(node: FunctionDeclaration) { + if (!exportedFunctionDeclarations) { + exportedFunctionDeclarations = []; + } + + exportedFunctionDeclarations.push(createDeclarationExport(node)); + } + + function hoistBindingElement(node: VariableDeclaration | BindingElement, isExported: boolean) { + let name = node.name; + if (isIdentifier(name)) { + hoistVariableDeclaration(getSynthesizedNode(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 = cloneNode(node, node, node.flags, /*parent*/ undefined, node); + updated.statements = createNodeArray(statements, node.statements); + return updated; + } } } \ No newline at end of file diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index aa3c860a9815e..dde67177a0f05 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1240,6 +1240,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) { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 8f5cb736dbf0f..b9e8854aadfac 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -663,13 +663,18 @@ namespace ts { export function flattenNodes(nodes: OneOrMany[]): T[] { let result: T[]; if (nodes) { - result = []; - for (const node of nodes) { - addNode(result, node); + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + if (result || node === undefined || isNodeArrayNode(node)) { + if (!result) { + result = nodes.slice(0, i); + } + addNode(result, node); + } } } - return result; + return result || nodes; } /** @@ -678,8 +683,9 @@ namespace ts { * @param to The destination array. * @param from The source Node or NodeArrayNode. */ - export function addNode(to: T[], from: OneOrMany, startOnNewLine?: boolean) { + export function addNode(to: T[], from: OneOrMany, startOnNewLine?: boolean): T[] { addNodeWorker(to, from, startOnNewLine, /*test*/ undefined) + return to; } /** @@ -688,8 +694,9 @@ namespace ts { * @param to The destination NodeArray. * @param from The source array of Node or NodeArrayNode. */ - export function addNodes(to: T[], from: OneOrMany[], startOnNewLine?: boolean) { + export function addNodes(to: T[], from: OneOrMany[], startOnNewLine?: boolean): T[] { addNodesWorker(to, from, startOnNewLine, /*test*/ undefined); + return to; } /** @@ -698,8 +705,9 @@ namespace ts { * @param to The destination array. * @param from The source Node or NodeArrayNode. */ - export function addLine(to: T[], from: OneOrMany) { + export function addLine(to: T[], from: OneOrMany): T[] { addNodeWorker(to, from, /*addOnNewLine*/ true, /*test*/ undefined); + return to; } /** @@ -708,8 +716,9 @@ namespace ts { * @param to The destination NodeArray. * @param from The source array of Node or NodeArrayNode. */ - export function addLines(to: T[], from: OneOrMany[]) { + export function addLines(to: T[], from: OneOrMany[]): T[] { addNodesWorker(to, from, /*addOnNewLine*/ true, /*test*/ undefined); + return to; } function addNodeWorker(to: T[], from: OneOrMany, addOnNewLine: boolean, test: (node: Node) => boolean) { @@ -896,7 +905,7 @@ namespace ts { * * @param nodes The NodeArray. */ - function liftToBlock(nodes: NodeArray) { + export function liftToBlock(nodes: NodeArray) { Debug.assert(every(nodes, isStatement), "Cannot lift nodes to a Block."); return createBlock(>nodes); } From 6d273366d3d380b3e4bf0b2e70b5bde20955b06b Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 16:10:32 -0800 Subject: [PATCH 11/43] Merged some changes from other branches. --- Jakefile.js | 9 + src/compiler/comments.ts | 140 ++++++++----- src/compiler/declarationEmitter.ts | 8 +- src/compiler/emitter.ts | 58 ++++-- src/compiler/printer.ts | 324 ++++++++++++++++++----------- src/compiler/program.ts | 3 +- src/compiler/sourcemap.ts | 37 +++- src/compiler/types.ts | 20 +- src/compiler/utilities.ts | 52 +++-- 9 files changed, 432 insertions(+), 219 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 44cb1a61dbe65..7b9c4806e0a9d 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -222,6 +222,7 @@ function concatenateFiles(destinationFile, sourceFiles) { } var useDebugMode = true; +var useTransforms = process.env.USE_TRANSFORMS || false; var host = (process.env.host || process.env.TYPESCRIPT_HOST || "node"); var compilerFilename = "tsc.js"; var LKGCompiler = path.join(LKGDirectory, compilerFilename); @@ -281,6 +282,10 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, noOu options += " --stripInternal" } + if (useBuiltCompiler && useTransforms) { + options += " --experimentalTransforms" + } + var cmd = host + " " + compilerPath + " " + options + " "; cmd = cmd + sources.join(" "); console.log(cmd + "\n"); @@ -404,6 +409,10 @@ task("setDebugMode", function() { useDebugMode = true; }); +task("setTransforms", function() { + useTransforms = true; +}); + task("configure-nightly", [configureNightlyJs], function() { var cmd = host + " " + configureNightlyJs + " " + packageJson + " " + programTs; console.log(cmd); diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index b93b144bf01b4..a680d01e102a1 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -5,11 +5,15 @@ namespace ts { export interface CommentWriter { reset(): void; setSourceFile(sourceFile: SourceFile): void; - getLeadingCommentsToEmit(node: TextRange): CommentRange[]; - getTrailingCommentsToEmit(node: TextRange): CommentRange[]; - emitDetachedComments(node: TextRange): void; - emitLeadingComments(node: TextRange, comments?: CommentRange[]): void; - emitTrailingComments(node: TextRange, comments?: CommentRange[]): void; + getLeadingComments(range: Node, getAdditionalRange?: (range: Node) => Node): CommentRange[]; + getLeadingComments(range: TextRange): CommentRange[]; + getLeadingCommentsOfPosition(pos: number): CommentRange[]; + getTrailingComments(range: Node, getAdditionalRange?: (range: Node) => Node): CommentRange[]; + getTrailingComments(range: TextRange): CommentRange[]; + getTrailingCommentsOfPosition(pos: number): CommentRange[]; + emitLeadingComments(range: TextRange, comments?: CommentRange[]): void; + emitTrailingComments(range: TextRange, comments?: CommentRange[]): void; + emitDetachedComments(range: TextRange): void; } export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter { @@ -25,8 +29,8 @@ namespace ts { // This maps start->end for a comment range. See `hasConsumedCommentRange` and // `consumeCommentRange` for usage. let consumedCommentRanges: number[]; - let leadingCommentRangeNodeStarts: boolean[]; - let trailingCommentRangeNodeEnds: boolean[]; + let leadingCommentRangePositions: boolean[]; + let trailingCommentRangePositions: boolean[]; return compilerOptions.removeComments ? createCommentRemovingWriter() @@ -36,11 +40,13 @@ namespace ts { return { reset, setSourceFile, - getLeadingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; }, - getTrailingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; }, + getLeadingComments(range: TextRange, getAdditionalRange?: (range: TextRange) => TextRange): CommentRange[] { return undefined; }, + getLeadingCommentsOfPosition(pos: number): CommentRange[] { return undefined; }, + getTrailingComments(range: TextRange, getAdditionalRange?: (range: TextRange) => TextRange): CommentRange[] { return undefined; }, + getTrailingCommentsOfPosition(pos: number): CommentRange[] { return undefined; }, + emitLeadingComments(range: TextRange, comments?: CommentRange[]): void { }, + emitTrailingComments(range: TextRange, comments?: CommentRange[]): void { }, emitDetachedComments, - emitLeadingComments(node: TextRange, comments?: CommentRange[]): void { }, - emitTrailingComments(node: TextRange, comments?: CommentRange[]): void { }, }; function emitDetachedComments(node: TextRange): void { @@ -53,41 +59,85 @@ namespace ts { return { reset, setSourceFile, - getLeadingCommentsToEmit, - getTrailingCommentsToEmit, - emitDetachedComments, + getLeadingComments, + getLeadingCommentsOfPosition, + getTrailingComments, + getTrailingCommentsOfPosition, emitLeadingComments, emitTrailingComments, + emitDetachedComments, }; - function getLeadingCommentsToEmit(node: TextRange) { - if (nodeIsSynthesized(node)) { - return; + function getLeadingComments(range: TextRange | Node, getAdditionalRange?: (range: Node) => Node) { + let comments = getLeadingCommentsOfPosition(range.pos); + if (getAdditionalRange) { + let additionalRange = getAdditionalRange(range); + while (additionalRange) { + comments = concatenate( + getLeadingCommentsOfPosition(additionalRange.pos), + comments + ); + + additionalRange = getAdditionalRange(additionalRange); + } } - if (!leadingCommentRangeNodeStarts[node.pos]) { - leadingCommentRangeNodeStarts[node.pos] = true; - const comments = hasDetachedComments(node.pos) - ? getLeadingCommentsWithoutDetachedComments() - : getLeadingCommentRanges(currentText, node.pos); - return consumeCommentRanges(comments); + return comments; + } + + function getTrailingComments(range: TextRange | Node, getAdditionalRange?: (range: Node) => Node) { + let comments = getTrailingCommentsOfPosition(range.end); + if (getAdditionalRange) { + let additionalRange = getAdditionalRange(range); + while (additionalRange) { + comments = concatenate( + comments, + getTrailingCommentsOfPosition(additionalRange.end) + ); + + additionalRange = getAdditionalRange(additionalRange); + } } - return noComments; + return comments; } - function getTrailingCommentsToEmit(node: TextRange) { - if (nodeIsSynthesized(node)) { - return; + function getLeadingCommentsOfPosition(pos: number) { + if (positionIsSynthesized(pos) || leadingCommentRangePositions[pos]) { + return undefined; } - if (!trailingCommentRangeNodeEnds[node.end]) { - trailingCommentRangeNodeEnds[node.end] = true; - const comments = getTrailingCommentRanges(currentText, node.end); - return consumeCommentRanges(comments); + leadingCommentRangePositions[pos] = true; + const comments = hasDetachedComments(pos) + ? getLeadingCommentsWithoutDetachedComments() + : getLeadingCommentRanges(currentText, pos); + return consumeCommentRanges(comments); + } + + function getTrailingCommentsOfPosition(pos: number) { + if (positionIsSynthesized(pos) || trailingCommentRangePositions[pos]) { + return undefined; } - return noComments; + trailingCommentRangePositions[pos] = true; + const comments = getTrailingCommentRanges(currentText, pos); + return consumeCommentRanges(comments); + } + + function emitLeadingComments(range: TextRange, comments = getLeadingComments(range)) { + emitNewLineBeforeLeadingComments(currentLineMap, writer, range, comments); + + // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space + emitComments(currentText, currentLineMap, writer, comments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); + } + + function emitTrailingComments(range: TextRange, comments = getTrailingComments(range)) { + // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ + emitComments(currentText, currentLineMap, writer, comments, /*leadingSeparator*/ true, /*trailingSeparator*/ false, newLine, writeComment); + } + + function emitDetachedComments(range: TextRange) { + emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false); } function hasConsumedCommentRange(comment: CommentRange) { @@ -136,22 +186,6 @@ namespace ts { return noComments; } - - function emitLeadingComments(range: TextRange, leadingComments: CommentRange[] = getLeadingCommentsToEmit(range)) { - emitNewLineBeforeLeadingComments(currentLineMap, writer, range, leadingComments); - - // Leading comments are emitted as `/*leading comment1 */space/*leading comment*/space` - emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); - } - - function emitTrailingComments(range: TextRange, trailingComments = getTrailingCommentsToEmit(range)) { - // Trailing comments are emitted as `space/*trailing comment1 */space/*trailing comment*/` - emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment); - } - - function emitDetachedComments(range: TextRange) { - emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false); - } } function reset() { @@ -160,8 +194,8 @@ namespace ts { currentLineMap = undefined; detachedCommentsInfo = undefined; consumedCommentRanges = undefined; - trailingCommentRangeNodeEnds = undefined; - leadingCommentRangeNodeStarts = undefined; + trailingCommentRangePositions = undefined; + leadingCommentRangePositions = undefined; } function setSourceFile(sourceFile: SourceFile) { @@ -170,8 +204,8 @@ namespace ts { currentLineMap = getLineStarts(sourceFile); detachedCommentsInfo = undefined; consumedCommentRanges = []; - leadingCommentRangeNodeStarts = []; - trailingCommentRangeNodeEnds = []; + leadingCommentRangePositions = []; + trailingCommentRangePositions = []; } function hasDetachedComments(pos: number) { @@ -182,7 +216,7 @@ namespace ts { // get the leading comments from detachedPos const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos; const leadingComments = getLeadingCommentRanges(currentText, pos); - if (detachedCommentsInfo.length > 1) { + if (detachedCommentsInfo.length - 1) { detachedCommentsInfo.pop(); } else { diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index ab0b16947fc49..c89b7579d5247 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -91,7 +91,7 @@ namespace ts { // Emit reference in dts, if the file reference was not already emitted if (referencedFile && !contains(emittedReferencedFiles, referencedFile)) { // Add a reference to generated dts file, - // global file reference is added only + // global file reference is added only // - if it is not bundled emit (because otherwise it would be self reference) // - and it is not already added if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference)) { @@ -144,7 +144,7 @@ namespace ts { if (!isBundledEmit && isExternalModule(sourceFile) && sourceFile.moduleAugmentations.length && !resultHasExternalModuleIndicator) { // if file was external module with augmentations - this fact should be preserved in .d.ts as well. - // in case if we didn't write any external module specifiers in .d.ts we need to emit something + // in case if we didn't write any external module specifiers in .d.ts we need to emit something // that will force compiler to think that this file is an external module - 'export {}' is a reasonable choice here. write("export {};"); writeLine(); @@ -349,7 +349,7 @@ namespace ts { const jsDocComments = getJsDocCommentsFromText(declaration, currentText); emitNewLineBeforeLeadingComments(currentLineMap, writer, declaration, jsDocComments); // jsDoc comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, jsDocComments, /*trailingSeparator*/ true, newLine, writeCommentRange); + emitComments(currentText, currentLineMap, writer, jsDocComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeCommentRange); } } @@ -736,7 +736,7 @@ namespace ts { function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) { // emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations). - // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered + // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered // external modules since they are indistingushable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}' // so compiler will treat them as external modules. resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index fb9c70f9a7c6f..3919e68aba5d9 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1915,7 +1915,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge if (multiLine) { decreaseIndent(); - writeLine(); } write(")"); @@ -2242,6 +2241,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return node; } + function skipAssertions(node: Expression): Expression { + while (node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression) { + node = (node).expression; + } + return node; + } + function emitCallTarget(node: Expression): Expression { if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword) { emit(node); @@ -3308,8 +3314,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge // we can't reuse 'arr' because it might be modified within the body of the loop. const counter = createTempVariable(TempFlags._i); const rhsReference = createSynthesizedNode(SyntaxKind.Identifier) as Identifier; - rhsReference.text = node.expression.kind === SyntaxKind.Identifier ? - makeUniqueName((node.expression).text) : + const expressionWithoutAssertions = skipAssertions(node.expression); + rhsReference.text = expressionWithoutAssertions.kind === SyntaxKind.Identifier ? + makeUniqueName((expressionWithoutAssertions).text) : makeTempVariableName(TempFlags.Auto); // This is the let keyword for the counter and rhsReference. The let keyword for @@ -4324,7 +4331,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge writeLine(); emitStart(restParam); emitNodeWithCommentsAndWithoutSourcemap(restParam.name); - write("[" + tempName + " - " + restIndex + "] = arguments[" + tempName + "];"); + write(restIndex > 0 + ? `[${tempName} - ${restIndex}] = arguments[${tempName}];` + : `[${tempName}] = arguments[${tempName}];`); emitEnd(restParam); decreaseIndent(); writeLine(); @@ -5340,6 +5349,18 @@ const _super = (function (geti, seti) { write(" = "); } + const staticProperties = getInitializedProperties(node, /*isStatic*/ true); + const isClassExpressionWithStaticProperties = staticProperties.length > 0 && node.kind === SyntaxKind.ClassExpression; + let tempVariable: Identifier; + + if (isClassExpressionWithStaticProperties) { + tempVariable = createAndRecordTempVariable(TempFlags.Auto); + write("("); + increaseIndent(); + emit(tempVariable); + write(" = "); + } + write("(function ("); const baseTypeNode = getClassExtendsHeritageClauseElement(node); if (baseTypeNode) { @@ -5369,9 +5390,6 @@ const _super = (function (geti, seti) { writeLine(); emitConstructor(node, baseTypeNode); emitMemberFunctionsForES5AndLower(node); - emitPropertyDeclarations(node, getInitializedProperties(node, /*isStatic*/ true)); - writeLine(); - emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); writeLine(); emitToken(SyntaxKind.CloseBraceToken, node.members.end, () => { write("return "); @@ -5398,7 +5416,23 @@ const _super = (function (geti, seti) { write("))"); if (node.kind === SyntaxKind.ClassDeclaration) { write(";"); + emitPropertyDeclarations(node, staticProperties); + writeLine(); + emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); } + else if (isClassExpressionWithStaticProperties) { + for (const property of staticProperties) { + write(","); + writeLine(); + emitPropertyDeclaration(node, property, /*receiver*/ tempVariable, /*isExpression*/ true); + } + write(","); + writeLine(); + emit(tempVariable); + decreaseIndent(); + write(")"); + } + emitEnd(node); if (node.kind === SyntaxKind.ClassDeclaration) { @@ -7937,7 +7971,7 @@ const _super = (function (geti, seti) { emitNewLineBeforeLeadingComments(currentLineMap, writer, node, leadingComments); // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); + emitComments(currentText, currentLineMap, writer, leadingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); } function emitTrailingComments(node: Node) { @@ -7949,7 +7983,7 @@ const _super = (function (geti, seti) { const trailingComments = getTrailingCommentsToEmit(node); // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ - emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment); + emitComments(currentText, currentLineMap, writer, trailingComments, /*leadingSeparator*/ true, /*trailingSeparator*/ false, newLine, writeComment); } /** @@ -7964,8 +7998,8 @@ const _super = (function (geti, seti) { const trailingComments = getTrailingCommentRanges(currentText, pos); - // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ - emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ true, newLine, writeComment); + // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space + emitComments(currentText, currentLineMap, writer, trailingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); } function emitLeadingCommentsOfPositionWorker(pos: number) { @@ -7986,7 +8020,7 @@ const _super = (function (geti, seti) { emitNewLineBeforeLeadingComments(currentLineMap, writer, { pos: pos, end: pos }, leadingComments); // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); + emitComments(currentText, currentLineMap, writer, leadingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); } function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange) { diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 84388d4fd8895..c69012441868c 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -75,6 +75,15 @@ function __export(m) { } })`; + const superHelper = ` +const _super = name => super[name];`; + + const advancedSuperHelper = ` +const _super = (function (geti, seti) { + const cache = Object.create(null); + return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); +})(name => super[name], (name, value) => super[name] = value);`; + const compilerOptions = host.getCompilerOptions(); const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); @@ -110,20 +119,41 @@ function __export(m) { const transformers = getTransformers(compilerOptions).concat(initializePrinter); const writer = createTextWriter(newLine); - const { write, writeTextOfNode, writeLine, increaseIndent, decreaseIndent } = writer; + const { + write, + writeTextOfNode, + writeLine, + increaseIndent, + decreaseIndent + } = writer; const sourceMap = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? createSourceMapWriter(host, writer) : getNullSourceMapWriter(); - const { emitStart, emitEnd, emitPos } = sourceMap; + const { + emitStart, + emitEnd, + emitPos + } = sourceMap; const comments = createCommentWriter(host, writer, sourceMap); - const { emitDetachedComments, emitLeadingComments, emitTrailingComments, getLeadingCommentsToEmit, getTrailingCommentsToEmit } = comments; + const { + getLeadingComments, + getTrailingComments, + getTrailingCommentsOfPosition, + emitLeadingComments, + emitTrailingComments, + emitDetachedComments + } = comments; let context: TransformationContext; let startLexicalEnvironment: () => void; let endLexicalEnvironment: () => Statement[]; let getNodeEmitFlags: (node: Node) => NodeEmitFlags; + let isExpressionSubstitutionEnabled: (node: Node) => boolean; + let isEmitNotificationEnabled: (node: Node) => boolean; let expressionSubstitution: (node: Expression) => Expression; let identifierSubstitution: (node: Identifier) => Identifier; + let onBeforeEmitNode: (node: Node) => void; + let onAfterEmitNode: (node: Node) => void; let isUniqueName: (name: string) => boolean; let temporaryVariables: string[] = []; let tempFlags: TempFlags; @@ -177,8 +207,12 @@ function __export(m) { startLexicalEnvironment = undefined; endLexicalEnvironment = undefined; getNodeEmitFlags = undefined; + isExpressionSubstitutionEnabled = undefined; + isEmitNotificationEnabled = undefined; expressionSubstitution = undefined; identifierSubstitution = undefined; + onBeforeEmitNode = undefined; + onAfterEmitNode = undefined; isUniqueName = undefined; temporaryVariables = undefined; tempFlags = 0; @@ -196,8 +230,12 @@ function __export(m) { startLexicalEnvironment = context.startLexicalEnvironment; endLexicalEnvironment = context.endLexicalEnvironment; getNodeEmitFlags = context.getNodeEmitFlags; + isExpressionSubstitutionEnabled = context.isExpressionSubstitutionEnabled; + isEmitNotificationEnabled = context.isEmitNotificationEnabled; expressionSubstitution = context.expressionSubstitution; identifierSubstitution = context.identifierSubstitution; + onBeforeEmitNode = context.onBeforeEmitNode; + onAfterEmitNode = context.onAfterEmitNode; isUniqueName = context.isUniqueName; return printSourceFile; } @@ -212,18 +250,46 @@ function __export(m) { } function emit(node: Node) { + emitWithWorker(node, emitWorker); + } + + function emitExpression(node: Expression) { + emitWithWorker(node, emitExpressionWorker); + } + + function emitWithWorker(node: Node, emitWorker: (node: Node) => void) { if (node) { - const leadingComments = getLeadingCommentsToEmit(node); - const trailingComments = getTrailingCommentsToEmit(node); + const adviseOnEmit = isEmitNotificationEnabled(node); + if (adviseOnEmit && onBeforeEmitNode) { + onBeforeEmitNode(node); + } + + const leadingComments = getLeadingComments(node, getNotEmittedParent); + const trailingComments = getTrailingComments(node, getNotEmittedParent); emitLeadingComments(node, leadingComments); emitStart(node); emitWorker(node); emitEnd(node); emitTrailingComments(node, trailingComments); + + if (adviseOnEmit && onAfterEmitNode) { + onAfterEmitNode(node); + } } } - function emitWorker(node: Node) { + function getNotEmittedParent(node: Node): Node { + if (getNodeEmitFlags(node) & NodeEmitFlags.EmitCommentsOfNotEmittedParent) { + const parent = getOriginalNode(node).parent; + if (getNodeEmitFlags(parent) & NodeEmitFlags.IsNotEmittedNode) { + return parent; + } + } + + return undefined; + } + + function emitWorker(node: Node): void { const kind = node.kind; switch (kind) { // Pseudo-literals @@ -234,7 +300,11 @@ function __export(m) { // Identifiers case SyntaxKind.Identifier: - return emitIdentifier(node, identifierSubstitution); + if (tryEmitSubstitute(node, identifierSubstitution)) { + return; + } + + return emitIdentifier(node); // Reserved words case SyntaxKind.ConstKeyword: @@ -324,7 +394,7 @@ function __export(m) { case SyntaxKind.ExpressionWithTypeArguments: return emitExpressionWithTypeArguments(node); case SyntaxKind.ThisType: - return write("this"); + return emitThisType(node); case SyntaxKind.StringLiteralType: return emitLiteral(node); @@ -340,7 +410,7 @@ function __export(m) { case SyntaxKind.TemplateSpan: return emitTemplateSpan(node); case SyntaxKind.SemicolonClassElement: - return write(";"); + return emitSemicolonClassElement(node); // Statements case SyntaxKind.Block: @@ -348,7 +418,7 @@ function __export(m) { case SyntaxKind.VariableStatement: return emitVariableStatement(node); case SyntaxKind.EmptyStatement: - return write(";"); + return emitEmptyStatement(node); case SyntaxKind.ExpressionStatement: return emitExpressionStatement(node); case SyntaxKind.IfStatement: @@ -467,25 +537,17 @@ function __export(m) { // JSDoc nodes (ignored) } - if (isExpressionKind(kind)) { + if (isExpression(node)) { return emitExpressionWorker(node); } } - function emitExpression(node: Expression) { - if (node) { - const leadingComments = getLeadingCommentsToEmit(node); - const trailingComments = getTrailingCommentsToEmit(node); - emitLeadingComments(node, leadingComments); - emitStart(node); - emitExpressionWorker(node); - emitEnd(node); - emitTrailingComments(node, trailingComments); - } - } - function emitExpressionWorker(node: Node) { const kind = node.kind; + if (isExpressionSubstitutionEnabled(node) && tryEmitSubstitute(node, expressionSubstitution)) { + return; + } + switch (kind) { // Literals case SyntaxKind.NumericLiteral: @@ -496,16 +558,15 @@ function __export(m) { // Identifiers case SyntaxKind.Identifier: - return emitIdentifier(node, expressionSubstitution); + return emitIdentifier(node); // Reserved words case SyntaxKind.FalseKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.TrueKeyword: - return writeTokenNode(node); case SyntaxKind.ThisKeyword: - return emitThisKeyword(node); + return writeTokenNode(node); // Expressions case SyntaxKind.ArrayLiteralExpression: @@ -597,17 +658,14 @@ function __export(m) { // Identifiers // - function emitIdentifier(node: Identifier, substitution: (node: Node) => Node) { - if (tryEmitSubstitute(node, substitution)) { - return; - } - else if (node.text === undefined) { + function emitIdentifier(node: Identifier) { + if (node.text === undefined) { // Emit a temporary variable name for this node. const nodeId = getOriginalNodeId(node); const text = temporaryVariables[nodeId] || (temporaryVariables[nodeId] = makeTempVariableName(tempKindToFlags(node.tempKind))); write(text); } - else if (nodeIsSynthesized(node)) { + else if (nodeIsSynthesized(node) || !node.parent) { if (getNodeEmitFlags(node) & NodeEmitFlags.UMDDefine) { writeLines(umdHelper); } @@ -620,15 +678,6 @@ function __export(m) { } } - function emitThisKeyword(node: PrimaryExpression) { - if (tryEmitSubstitute(node, expressionSubstitution)) { - return; - } - else { - writeTokenNode(node); - } - } - // // Names // @@ -716,13 +765,13 @@ function __export(m) { emitModifiers(node, node.modifiers); writeIfPresent(node.asteriskToken, "*"); emit(node.name); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } function emitConstructor(node: ConstructorDeclaration) { emitModifiers(node, node.modifiers); write("constructor"); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } function emitAccessorDeclaration(node: AccessorDeclaration) { @@ -730,7 +779,7 @@ function __export(m) { emitModifiers(node, node.modifiers); write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); emit(node.name); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } function emitCallSignature(node: CallSignatureDeclaration) { @@ -760,6 +809,10 @@ function __export(m) { write(";"); } + function emitSemicolonClassElement(node: SemicolonClassElement) { + write(";"); + } + // // Types // @@ -826,6 +879,10 @@ function __export(m) { write(")"); } + function emitThisType(node: ThisTypeNode) { + write("this"); + } + // // Binding patterns // @@ -871,7 +928,7 @@ function __export(m) { write("[]"); } else { - const preferNewLine = getNodeEmitFlags(node) & NodeEmitFlags.MultiLine ? ListFormat.PreferNewLine : ListFormat.None; + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine); } } @@ -882,7 +939,7 @@ function __export(m) { write("{}"); } else { - const preferNewLine = getNodeEmitFlags(node) & NodeEmitFlags.MultiLine ? ListFormat.PreferNewLine : ListFormat.None; + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None; emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); } @@ -973,27 +1030,8 @@ function __export(m) { function emitArrowFunction(node: ArrowFunction) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); - const body = node.body; - if (isBlock(body)) { - const savedTempFlags = tempFlags; - tempFlags = 0; - startLexicalEnvironment(); - emitArrowFunctionHead(node); - write(" {"); - - const startingLine = writer.getLine(); - emitBlockFunctionBody(node, body); + emitSignatureAndBody(node, emitArrowFunctionHead); - const endingLine = writer.getLine(); - emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); - tempFlags = savedTempFlags; - write("}"); - } - else { - emitArrowFunctionHead(node); - write(" "); - emitExpression(body); - } } function emitArrowFunctionHead(node: ArrowFunction) { @@ -1051,19 +1089,11 @@ function __export(m) { } function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { - if (tryEmitSubstitute(node, expressionSubstitution)) { - return; - } - emitExpression(node.operand); writeToken(node.operator); } function emitBinaryExpression(node: BinaryExpression) { - if (tryEmitSubstitute(node, expressionSubstitution)) { - return; - } - const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; const indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); @@ -1169,6 +1199,10 @@ function __export(m) { write(";"); } + function emitEmptyStatement(node: EmptyStatement) { + write(";"); + } + function emitExpressionStatement(node: ExpressionStatement) { emitExpression(node.expression); write(";"); @@ -1336,25 +1370,27 @@ function __export(m) { emitModifiers(node, node.modifiers); write(node.asteriskToken ? "function* " : "function "); emit(node.name); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } - function emitSignatureAndBody(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) { + function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { const body = node.body; if (body) { - const savedTempFlags = tempFlags; - tempFlags = 0; - startLexicalEnvironment(); - emitSignatureHead(node); - write(" {"); - - const startingLine = writer.getLine(); - emitBlockFunctionBody(node, body); - - const endingLine = writer.getLine(); - emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); - write("}"); - tempFlags = savedTempFlags; + if (isBlock(body)) { + const savedTempFlags = tempFlags; + tempFlags = 0; + startLexicalEnvironment(); + emitSignatureHead(node); + write(" {"); + emitBlockFunctionBody(node, body); + write("}"); + tempFlags = savedTempFlags; + } + else { + emitSignatureHead(node); + write(" "); + emitExpression(body); + } } else { emitSignatureHead(node); @@ -1371,34 +1407,49 @@ function __export(m) { function shouldEmitBlockFunctionBodyOnSingleLine(parentNode: Node, body: Block) { const originalNode = getOriginalNode(parentNode); - if (isFunctionLike(originalNode) && !nodeIsSynthesized(originalNode) && rangeEndIsOnSameLineAsRangeStart(originalNode.body, originalNode.body)) { - for (const statement of body.statements) { - if (synthesizedNodeStartsOnNewLine(statement)) { - return false; + if (isFunctionLike(originalNode) && !nodeIsSynthesized(originalNode)) { + const body = originalNode.body; + if (isBlock(body)) { + if (rangeEndIsOnSameLineAsRangeStart(body, body)) { + for (const statement of body.statements) { + if (synthesizedNodeStartsOnNewLine(statement)) { + return false; + } + } + + return true; } } - - if (originalNode.kind === SyntaxKind.ArrowFunction && !rangeEndIsOnSameLineAsRangeStart((originalNode).equalsGreaterThanToken, originalNode.body)) { - return false; + else { + return rangeEndIsOnSameLineAsRangeStart((originalNode).equalsGreaterThanToken, originalNode.body); } - - return true; } return false; } function emitBlockFunctionBody(parentNode: Node, body: Block) { + const startingLine = writer.getLine(); + increaseIndent(); + emitDetachedComments(body.statements); + // Emit all the prologue directives (like "use strict"). - const statements = body.statements; - const statementOffset = emitPrologueDirectives(statements, /*startWithNewLine*/ true, /*indented*/ true); + const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true); const helpersEmitted = emitHelpers(body); + if (statementOffset === 0 && !helpersEmitted && shouldEmitBlockFunctionBodyOnSingleLine(parentNode, body)) { - emitList(body, statements, ListFormat.SingleLineFunctionBodyStatements); + decreaseIndent(); + emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); + increaseIndent(); } else { - emitList(body, statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); + emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); } + + const endingLine = writer.getLine(); + emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); + emitLeadingComments(collapseTextRange(body.statements, TextRangeCollapse.CollapseToEnd)); + decreaseIndent(); } function emitClassDeclaration(node: ClassDeclaration) { @@ -1668,6 +1719,8 @@ function __export(m) { write("case "); emitExpression(node.expression); write(":"); + + debugger; emitCaseOrDefaultClauseStatements(node, node.statements); } @@ -1688,6 +1741,7 @@ function __export(m) { function emitHeritageClause(node: HeritageClause) { emitStart(node); + write(" "); writeToken(node.token); write(" "); emitList(node, node.types, ListFormat.HeritageClauseTypes); @@ -1783,8 +1837,7 @@ function __export(m) { } } - function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean, indented?: boolean) { - increaseIndentIf(indented); + function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean) { for (let i = 0; i < statements.length; i++) { if (isPrologueDirective(statements[i])) { if (startWithNewLine || i > 0) { @@ -1794,12 +1847,10 @@ function __export(m) { } else { // return index of the first non prologue directive - decreaseIndentIf(indented); return i; } } - decreaseIndentIf(indented); return statements.length; } @@ -1811,7 +1862,17 @@ function __export(m) { } if (emitFlags & NodeEmitFlags.EmitExportStar) { - emitExportStarHelper(); + writeLines(exportStarHelper); + helpersEmitted = true; + } + + if (emitFlags & NodeEmitFlags.EmitSuperHelper) { + writeLines(superHelper); + helpersEmitted = true; + } + + if (emitFlags & NodeEmitFlags.EmitAdvancedSuperHelper) { + writeLines(advancedSuperHelper); helpersEmitted = true; } @@ -1861,10 +1922,6 @@ function __export(m) { return helpersEmitted; } - function emitExportStarHelper() { - writeLines(exportStarHelper); - } - function writeLines(text: string): void { const lines = text.split(/\r\n|\r|\n/g); for (let i = 0; i < lines.length; i++) { @@ -2027,8 +2084,10 @@ function __export(m) { } else { // Write the opening line terminator or leading whitespace. + let shouldEmitInterveningComments = true; if (shouldWriteLeadingLineTerminator(parentNode, children, format)) { writeLine(); + shouldEmitInterveningComments = false; } else if (format & ListFormat.SpaceBetweenBraces) { write(" "); @@ -2052,12 +2111,20 @@ function __export(m) { // Write either a line terminator or whitespace to separate the elements. if (shouldWriteSeparatingLineTerminator(previousSibling, child, format)) { writeLine(); + shouldEmitInterveningComments = false; } else if (previousSibling) { write(" "); } } + if (shouldEmitInterveningComments) { + emitLeadingComments(child, getTrailingCommentsOfPosition(child.pos)); + } + else { + shouldEmitInterveningComments = true; + } + // Emit this child. emit(child); @@ -2151,7 +2218,7 @@ function __export(m) { return true; } else if (format & ListFormat.PreserveLines) { - if (getNodeEmitFlags(parentNode) & NodeEmitFlags.MultiLine) { + if (format & ListFormat.PreferNewLine) { return true; } @@ -2193,10 +2260,10 @@ function __export(m) { function shouldWriteClosingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { if (format & ListFormat.MultiLine) { - return true; + return (format & ListFormat.NoTrailingNewLine) === 0; } else if (format & ListFormat.PreserveLines) { - if (getNodeEmitFlags(parentNode) & NodeEmitFlags.MultiLine) { + if (format & ListFormat.PreferNewLine) { return true; } @@ -2218,7 +2285,7 @@ function __export(m) { function synthesizedNodeStartsOnNewLine(node: Node, format?: ListFormat) { if (nodeIsSynthesized(node)) { - const startsOnNewLine = (node).startsOnNewLine; + const startsOnNewLine = node.startsOnNewLine; if (startsOnNewLine === undefined) { return (format & ListFormat.PreferNewLine) !== 0; } @@ -2250,8 +2317,12 @@ function __export(m) { } function needsIndentation(parent: Node, node1: Node, node2: Node): boolean { + parent = skipSynthesizedParentheses(parent); + node1 = skipSynthesizedParentheses(node1); + node2 = skipSynthesizedParentheses(node2); + // Always use a newline for synthesized code if the synthesizer desires it. - if (synthesizedNodeStartsOnNewLine(node2)) { + if (node2.startsOnNewLine) { return true; } @@ -2261,6 +2332,14 @@ function __export(m) { && !rangeEndIsOnSameLineAsRangeStart(node1, node2); } + function skipSynthesizedParentheses(node: Node) { + while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) { + node = (node).expression; + } + + return node; + } + function getTextOfNode(node: Node, includeTrivia?: boolean) { if (nodeIsSynthesized(node) && (isLiteralExpression(node) || isIdentifier(node))) { return node.text; @@ -2280,9 +2359,9 @@ function __export(m) { } function isSingleLineEmptyBlock(block: Block) { - return (getNodeEmitFlags(block) & NodeEmitFlags.MultiLine) === 0 && - block.statements.length === 0 && - rangeEndIsOnSameLineAsRangeStart(block, block); + return !block.multiLine + && block.statements.length === 0 + && rangeEndIsOnSameLineAsRangeStart(block, block); } function tempKindToFlags(kind: TempVariableKind) { @@ -2383,6 +2462,7 @@ function __export(m) { // Other PreferNewLine = 1 << 15, // Prefer adding a LineTerminator between synthesized nodes. + NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list. // Precomputed Formats TypeLiteralMembers = MultiLine | Indented, @@ -2400,7 +2480,7 @@ function __export(m) { MultiLineBlockStatements = Indented | MultiLine, VariableDeclarationList = CommaDelimited | SingleLine, SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenBraces, - MultiLineFunctionBodyStatements = MultiLine | Indented, + MultiLineFunctionBodyStatements = MultiLine, ClassHeritageClauses = SingleLine, ClassMembers = Indented | MultiLine, InterfaceMembers = Indented | MultiLine, @@ -2409,9 +2489,9 @@ function __export(m) { NamedImportsOrExportsElements = CommaDelimited | AllowTrailingComma | SingleLine | SpaceBetweenBraces, JsxElementChildren = SingleLine, JsxElementAttributes = SingleLine, - CaseOrDefaultClauseStatements = Indented | MultiLine, + CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty, HeritageClauseTypes = CommaDelimited | SingleLine, - SourceFileStatements = MultiLine, + SourceFileStatements = MultiLine | NoTrailingNewLine, Decorators = MultiLine | Optional, TypeArguments = CommaDelimited | SingleLine | Indented | AngleBrackets | Optional, TypeParameters = CommaDelimited | SingleLine | Indented | AngleBrackets | Optional, diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4a3d0cbc164bf..f10c327faa706 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -968,7 +968,8 @@ namespace ts { const start = new Date().getTime(); - const emitResult = emitFiles( + const fileEmitter = options.experimentalTransforms ? printFiles : emitFiles; + const emitResult = fileEmitter( emitResolver, getEmitHost(writeFileCallback), sourceFile); diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 86bad38cb6e1f..3866e4f956636 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -7,12 +7,15 @@ namespace ts { setSourceFile(sourceFile: SourceFile): void; emitPos(pos: number): void; emitStart(range: TextRange): void; - emitEnd(range: TextRange, stopOverridingSpan?: boolean): void; - changeEmitSourcePos(): void; + emitEnd(range: TextRange): void; + /*@deprecated*/ emitEnd(range: TextRange, stopOverridingSpan: boolean): void; + /*@deprecated*/ changeEmitSourcePos(): void; getText(): string; getSourceMappingURL(): string; initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void; reset(): void; + enable(): void; + disable(): void; } let nullSourceMapWriter: SourceMapWriter; @@ -38,6 +41,8 @@ namespace ts { getSourceMappingURL(): string { return undefined; }, initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void { }, reset(): void { }, + enable(): void { }, + disable(): void { } }; } @@ -62,6 +67,8 @@ namespace ts { // Source map data let sourceMapData: SourceMapData; + let disableDepth: number; + return { getSourceMapData: () => sourceMapData, setSourceFile, @@ -73,6 +80,8 @@ namespace ts { getSourceMappingURL, initialize, reset, + enable, + disable, }; function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { @@ -81,6 +90,7 @@ namespace ts { } currentSourceFile = undefined; + disableDepth = 0; // Current source map file and its index in the sources list sourceMapSourceIndex = -1; @@ -147,6 +157,17 @@ namespace ts { lastEncodedSourceMapSpan = undefined; lastEncodedNameIndex = undefined; sourceMapData = undefined; + disableDepth = 0; + } + + function enable() { + if (disableDepth > 0) { + disableDepth--; + } + } + + function disable() { + disableDepth++; } function updateLastEncodedAndRecordedSpans() { @@ -168,7 +189,7 @@ namespace ts { sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] : defaultLastEncodedSourceMapSpan; - // TODO: Update lastEncodedNameIndex + // TODO: Update lastEncodedNameIndex // Since we dont support this any more, lets not worry about it right now. // When we start supporting nameIndex, we will get back to this @@ -236,7 +257,7 @@ namespace ts { } function emitPos(pos: number) { - if (pos === -1) { + if (positionIsSynthesized(pos) || disableDepth > 0) { return; } @@ -288,9 +309,17 @@ namespace ts { function emitStart(range: TextRange) { emitPos(getStartPos(range)); + + if (range.disableSourceMap) { + disable(); + } } function emitEnd(range: TextRange, stopOverridingEnd?: boolean) { + if (range.disableSourceMap) { + enable(); + } + emitPos(range.end); stopOverridingSpan = stopOverridingEnd; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1422b611651dd..2348c4d2b0389 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -20,6 +20,7 @@ namespace ts { export interface TextRange { pos: number; end: number; + /* @internal */ disableSourceMap?: boolean; // Whether a synthesized text range disables source maps for its contents (used by transforms). } // token > SyntaxKind.Identifer => token is a keyword @@ -2468,6 +2469,7 @@ namespace ts { allowSyntheticDefaultImports?: boolean; allowJs?: boolean; /* @internal */ stripInternal?: boolean; + /* @internal */ experimentalTransforms?: boolean; // Skip checking lib.d.ts to help speed up tests. /* @internal */ skipDefaultLibCheck?: boolean; @@ -2793,14 +2795,16 @@ namespace ts { /* @internal */ export const enum NodeEmitFlags { - EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node. - EmitExportStar = 1 << 1, // The export * helper should be written to this node. - EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods. - EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods. - UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. - NoLexicalEnvironment = 1 << 5, // A new LexicalEnvironment should *not* be introduced when emitting this node, this is primarily used when printing a SystemJS module. - SingleLine = 1 << 6, // The contents of this node should be emit on a single line. - AdviseOnEmitNode = 1 << 7, // The node printer should invoke the onBeforeEmitNode and onAfterEmitNode callbacks when printing this node. + EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node. + EmitExportStar = 1 << 1, // The export * helper should be written to this node. + EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods. + EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods. + UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. + NoLexicalEnvironment = 1 << 5, // A new LexicalEnvironment should *not* be introduced when emitting this node, this is primarily used when printing a SystemJS module. + SingleLine = 1 << 6, // The contents of this node should be emit on a single line. + 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 << 8, // Emits comments of missing parent nodes. } /** Additional context provided to `visitEachChild` */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 003b12280d342..10b5b467158f0 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2290,26 +2290,33 @@ namespace ts { } } - export function emitComments(text: string, lineMap: number[], writer: EmitTextWriter, comments: CommentRange[], trailingSeparator: boolean, newLine: string, + export function emitComments(text: string, lineMap: number[], writer: EmitTextWriter, comments: CommentRange[], leadingSeparator: boolean, trailingSeparator: boolean, newLine: string, writeComment: (text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) => void) { - let emitLeadingSpace = !trailingSeparator; - forEach(comments, comment => { - if (emitLeadingSpace) { + if (comments && comments.length > 0) { + if (leadingSeparator) { writer.write(" "); - emitLeadingSpace = false; } - writeComment(text, lineMap, writer, comment, newLine); - if (comment.hasTrailingNewLine) { - writer.writeLine(); + + let emitInterveningSeperator = false; + for (const comment of comments) { + if (emitInterveningSeperator) { + writer.write(" "); + emitInterveningSeperator = false; + } + + writeComment(text, lineMap, writer, comment, newLine); + if (comment.hasTrailingNewLine) { + writer.writeLine(); + } + else { + emitInterveningSeperator = true; + } } - else if (trailingSeparator) { + + if (emitInterveningSeperator && trailingSeparator) { writer.write(" "); } - else { - // Emit leading space to separate comment during next comment emit - emitLeadingSpace = true; - } - }); + } } /** @@ -2366,7 +2373,7 @@ namespace ts { if (nodeLine >= lastCommentLine + 2) { // Valid detachedComments emitNewLineBeforeLeadingComments(lineMap, writer, node, leadingComments); - emitComments(text, lineMap, writer, detachedComments, /*trailingSeparator*/ true, newLine, writeComment); + emitComments(text, lineMap, writer, detachedComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: lastOrUndefined(detachedComments).end }; } } @@ -2705,6 +2712,21 @@ 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.end } + : { pos: range.end, end: range.end }; + } + // Node tests // // All node tests in the following list should *not* reference parent pointers so that From f52a30be1bcc5c3cbce6b1f99b9bfefe0ea8b93a Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 16:11:31 -0800 Subject: [PATCH 12/43] Merged some changes from other branches. --- src/compiler/commandLineParser.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index d5bf95a64058a..c65466f00dede 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -320,6 +320,11 @@ namespace ts { name: "allowSyntheticDefaultImports", type: "boolean", description: Diagnostics.Allow_default_imports_from_modules_with_no_default_export_This_does_not_affect_code_emit_just_typechecking + }, + { + name: "experimentalTransforms", + type: "boolean", + experimental: true } ]; From 64e7aa8345f2fd394cc14df93c493fd6b4b95221 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 16:26:57 -0800 Subject: [PATCH 13/43] PR Feedback. --- src/compiler/printer.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index c69012441868c..a1d284dd42bba 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -215,7 +215,7 @@ const _super = (function (geti, seti) { onAfterEmitNode = undefined; isUniqueName = undefined; temporaryVariables = undefined; - tempFlags = 0; + tempFlags = TempFlags.Auto; currentSourceFile = undefined; currentText = undefined; extendsEmitted = false; @@ -1837,7 +1837,11 @@ const _super = (function (geti, seti) { } } - function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean) { + /** + * Emits any prologue directives at the start of a Statement list, returning the + * number of prologue directives written to the output. + */ + function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean): number { for (let i = 0; i < statements.length; i++) { if (isPrologueDirective(statements[i])) { if (startWithNewLine || i > 0) { From e5cd8fef21e9e2a618263675aca0185013c4537f Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 23 Feb 2016 11:35:26 -0800 Subject: [PATCH 14/43] Added temporary compat flag to control emit output to fix tests --- Jakefile.js | 8 ++++++++ src/compiler/commandLineParser.ts | 9 +++++++++ src/compiler/emitter.ts | 22 ++++++++++++++++------ src/compiler/program.ts | 1 + src/compiler/types.ts | 1 + 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 7b9c4806e0a9d..d1821b5b5e074 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -223,6 +223,7 @@ function concatenateFiles(destinationFile, sourceFiles) { var useDebugMode = true; var useTransforms = process.env.USE_TRANSFORMS || false; +var useTransformCompat = false; var host = (process.env.host || process.env.TYPESCRIPT_HOST || "node"); var compilerFilename = "tsc.js"; var LKGCompiler = path.join(LKGDirectory, compilerFilename); @@ -285,6 +286,9 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, noOu if (useBuiltCompiler && useTransforms) { options += " --experimentalTransforms" } + else if (useBuiltCompiler && useTransformCompat) { + options += " --transformCompatibleEmit" + } var cmd = host + " " + compilerPath + " " + options + " "; cmd = cmd + sources.join(" "); @@ -413,6 +417,10 @@ task("setTransforms", function() { useTransforms = true; }); +task("setTransformCompat", function() { + useTransformCompat = true; +}); + task("configure-nightly", [configureNightlyJs], function() { var cmd = host + " " + configureNightlyJs + " " + packageJson + " " + programTs; console.log(cmd); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index c65466f00dede..307bb5d540339 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -322,9 +322,18 @@ namespace ts { description: Diagnostics.Allow_default_imports_from_modules_with_no_default_export_This_does_not_affect_code_emit_just_typechecking }, { + // this option will be removed when this is merged with master and exists solely + // to enable the tree transforming emitter side-by-side with the existing emitter. name: "experimentalTransforms", type: "boolean", experimental: true + }, + { + // this option will be removed when this is merged with master and exists solely + // to enable the tree transforming emitter side-by-side with the existing emitter. + name: "transformCompatibleEmit", + type: "boolean", + experimental: true } ]; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 3919e68aba5d9..357fec7f0fd65 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1915,6 +1915,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge if (multiLine) { decreaseIndent(); + if (!compilerOptions.transformCompatibleEmit) { + writeLine(); + } } write(")"); @@ -4331,7 +4334,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge writeLine(); emitStart(restParam); emitNodeWithCommentsAndWithoutSourcemap(restParam.name); - write(restIndex > 0 + write(restIndex > 0 || !compilerOptions.transformCompatibleEmit ? `[${tempName} - ${restIndex}] = arguments[${tempName}];` : `[${tempName}] = arguments[${tempName}];`); emitEnd(restParam); @@ -5353,7 +5356,7 @@ const _super = (function (geti, seti) { const isClassExpressionWithStaticProperties = staticProperties.length > 0 && node.kind === SyntaxKind.ClassExpression; let tempVariable: Identifier; - if (isClassExpressionWithStaticProperties) { + if (isClassExpressionWithStaticProperties && compilerOptions.transformCompatibleEmit) { tempVariable = createAndRecordTempVariable(TempFlags.Auto); write("("); increaseIndent(); @@ -5390,6 +5393,11 @@ const _super = (function (geti, seti) { writeLine(); emitConstructor(node, baseTypeNode); emitMemberFunctionsForES5AndLower(node); + if (!compilerOptions.transformCompatibleEmit) { + emitPropertyDeclarations(node, staticProperties); + writeLine(); + emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); + } writeLine(); emitToken(SyntaxKind.CloseBraceToken, node.members.end, () => { write("return "); @@ -5416,11 +5424,13 @@ const _super = (function (geti, seti) { write("))"); if (node.kind === SyntaxKind.ClassDeclaration) { write(";"); - emitPropertyDeclarations(node, staticProperties); - writeLine(); - emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); + if (compilerOptions.transformCompatibleEmit) { + emitPropertyDeclarations(node, staticProperties); + writeLine(); + emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); + } } - else if (isClassExpressionWithStaticProperties) { + else if (isClassExpressionWithStaticProperties && compilerOptions.transformCompatibleEmit) { for (const property of staticProperties) { write(","); writeLine(); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index f10c327faa706..87e8775d1f0d9 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1,6 +1,7 @@ /// /// /// +/// namespace ts { /* @internal */ export let programTime = 0; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2348c4d2b0389..6a55153f63141 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2470,6 +2470,7 @@ namespace ts { allowJs?: boolean; /* @internal */ stripInternal?: boolean; /* @internal */ experimentalTransforms?: boolean; + /* @internal */ transformCompatibleEmit?: boolean; // Skip checking lib.d.ts to help speed up tests. /* @internal */ skipDefaultLibCheck?: boolean; From f484ff46c7ec66a786d04ed384fb7b51793b6701 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 23 Feb 2016 15:03:17 -0800 Subject: [PATCH 15/43] Reenabled transforms. --- src/compiler/transformer.ts | 34 +++++++++++++++++- src/compiler/transformers/ts.ts | 61 ++------------------------------- 2 files changed, 36 insertions(+), 59 deletions(-) diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 65d61c044c22a..913fd95d55dfc 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -1,15 +1,47 @@ /// +/// +/// +/// +/// +/// +/// +/// /* @internal */ namespace ts { + const moduleTransformerMap: Map = { + [ModuleKind.ES6]: transformES6Module, + [ModuleKind.System]: transformSystemModule, + [ModuleKind.AMD]: transformModule, + [ModuleKind.CommonJS]: transformModule, + [ModuleKind.UMD]: transformModule, + [ModuleKind.None]: transformModule, + }; + const enum SyntaxKindFeatureFlags { ExpressionSubstitution = 1 << 0, EmitNotifications = 1 << 1, } export function getTransformers(compilerOptions: CompilerOptions) { + const jsx = compilerOptions.jsx; + const languageVersion = getEmitScriptTarget(compilerOptions); + const moduleKind = getEmitModuleKind(compilerOptions); const transformers: Transformer[] = []; - // TODO(rbuckton): Add transformers + + transformers.push(transformTypeScript); + transformers.push(moduleTransformerMap[moduleKind]); + + if (jsx === JsxEmit.React) { + transformers.push(transformJsx); + } + + transformers.push(transformES7); + + if (languageVersion < ScriptTarget.ES6) { + transformers.push(transformES6); + } + return transformers; } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 8618e71f92188..e6572ee87097e 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -6,7 +6,6 @@ namespace ts { type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration; - // TODO(rbuckton): TS->ES7 transformer export function transformTypeScript(context: TransformationContext) { const { nodeHasGeneratedName, @@ -38,7 +37,6 @@ namespace ts { let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; let currentParent: Node; let currentNode: Node; - let isRightmostExpression: boolean; let superContainerStack: SuperContainer[]; return transformSourceFile; @@ -47,7 +45,6 @@ namespace ts { currentSourceFile = node; node = visitEachChild(node, visitor, context); setNodeEmitFlags(node, NodeEmitFlags.EmitEmitHelpers); - currentSourceFile = undefined; return node; } @@ -61,14 +58,12 @@ namespace ts { const savedCurrentScope = currentScope; const savedCurrentParent = currentParent; const savedCurrentNode = currentNode; - const savedIsRightmostExpression = isRightmostExpression; onBeforeVisitNode(node); node = visitor(node); currentNamespace = savedCurrentNamespace; currentScope = savedCurrentScope; currentParent = savedCurrentParent; currentNode = savedCurrentNode; - isRightmostExpression = savedIsRightmostExpression; return node; } @@ -334,11 +329,6 @@ namespace ts { currentScope = node; break; } - - // Keep track of whether this is the right-most expression of an expression tree. - // This is used to determine whether to parenthesize an `await` expression transformed - // into a `yield` expression. - isRightmostExpression = currentParent && isRightmost(currentParent, currentNode, isRightmostExpression); } /** @@ -567,7 +557,7 @@ namespace ts { * * @param node The node to transform. */ - function visitClassExpression(node: ClassExpression): LeftHandSideExpression { + function visitClassExpression(node: ClassExpression): Expression { const staticProperties = getInitializedProperties(node, /*isStatic*/ true); const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); const members = transformClassMembers(node, heritageClauses !== undefined); @@ -598,7 +588,7 @@ namespace ts { addNode(expressions, createAssignment(temp, classExpression)); addNodes(expressions, generateInitializedPropertyExpressions(node, staticProperties, temp)); addNode(expressions, temp); - return createParen(inlineExpressions(expressions)); + return inlineExpressions(expressions); } return classExpression; @@ -2229,12 +2219,10 @@ namespace ts { * @param node The await expression node. */ function visitAwaitExpression(node: AwaitExpression): Expression { - const expression = createYield( + return createYield( visitNode(node.expression, visitor, isExpression), node ); - - return isRightmostExpression ? expression : createParen(expression); } /** @@ -2690,48 +2678,5 @@ namespace ts { } } } - - function isRightmost(parentNode: Node, node: Node, wasRightmost: boolean) { - switch (parentNode.kind) { - case SyntaxKind.VariableDeclaration: - case SyntaxKind.Parameter: - case SyntaxKind.BindingElement: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertyAssignment: - case SyntaxKind.ShorthandPropertyAssignment: - case SyntaxKind.ExpressionStatement: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ThrowStatement: - case SyntaxKind.ReturnStatement: - case SyntaxKind.ForStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - case SyntaxKind.SwitchStatement: - case SyntaxKind.CaseClause: - case SyntaxKind.WithStatement: - case SyntaxKind.Decorator: - case SyntaxKind.ExpressionWithTypeArguments: - case SyntaxKind.SpreadElementExpression: - case SyntaxKind.AsExpression: - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.EnumMember: - case SyntaxKind.JsxAttribute: - case SyntaxKind.JsxSpreadAttribute: - case SyntaxKind.JsxExpression: - return true; - case SyntaxKind.TemplateSpan: - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - return node !== (parentNode).expression; - case SyntaxKind.BinaryExpression: - return wasRightmost && node === (parentNode).right; - case SyntaxKind.ConditionalExpression: - return wasRightmost && node === (parentNode).whenTrue; - default: - return wasRightmost; - } - } } } \ No newline at end of file From 02b85f8174a5a183ae0f1197fd3859e7741ec052 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 23 Feb 2016 15:36:57 -0800 Subject: [PATCH 16/43] PR feedback --- src/compiler/sourcemap.ts | 11 +++++ src/compiler/transformers/destructuring.ts | 57 ++++++++++++++-------- src/compiler/transformers/es7.ts | 2 - src/compiler/transformers/ts.ts | 4 +- src/compiler/utilities.ts | 2 +- 5 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 3866e4f956636..967fa0c715f2c 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -67,6 +67,11 @@ namespace ts { // Source map data let sourceMapData: SourceMapData; + // This keeps track of the number of times `disable` has been called without a + // corresponding call to `enable`. As long as this value is non-zero, mappings will not + // be recorded. + // This is primarily used to provide a better experience when debugging binding + // patterns and destructuring assignments for simple expressions. let disableDepth: number; return { @@ -160,12 +165,18 @@ namespace ts { disableDepth = 0; } + /** + * Re-enables the recording of mappings. + */ function enable() { if (disableDepth > 0) { disableDepth--; } } + /** + * Disables the recording of mappings. + */ function disable() { disableDepth++; } diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 05b2350226b97..0ada8d177b2ed 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -18,22 +18,29 @@ namespace ts { recordTempVariable: (node: Identifier) => void, visitor?: (node: Node) => Node) { - let location: TextRange = node; - let value = node.right; if (isEmptyObjectLiteralOrArrayLiteral(node.left)) { - return value; + return node.right; } + let location: TextRange = node; + let value = node.right; const expressions: Expression[] = []; if (needsValue) { - // Temporary assignment needed to emit root should highlight whole binary expression - value = ensureIdentifier(node.right, /*reuseIdentifierExpressions*/ true, node, emitTempVariableAssignment); + // If the right-hand value of the destructuring assignment needs to be preserved (as + // is the case when the destructuring assignmen) is part of a larger expression), + // then we need to cache the right-hand value. + // + // The source map location for the assignment should point to the entire binary + // expression. + value = ensureIdentifier(node.right, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); } else if (nodeIsSynthesized(node)) { - // Source map node for root.left = root.right is root - // but if root is synthetic, which could be in below case, use the target which is { a } - // for ({a} of {a: string}) { - // } + // Generally, the source map location for a destructuring assignment is the root + // expression. + // + // However, if the root expression is synthesized (as in the case + // of the initializer when transforming a ForOfStatement), then the source map + // location should point to the right-hand value of the expression. location = node.right; } @@ -255,21 +262,22 @@ namespace ts { function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) { const elements = target.elements; - if (elements.length !== 1) { + const numElements = elements.length; + if (numElements !== 1) { // For anything but a single element destructuring we need to generate a temporary // to ensure value is evaluated exactly once. // When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); } - for (let i = 0; i < elements.length; i++) { + for (let i = 0; i < numElements; i++) { const e = elements[i]; if (e.kind !== SyntaxKind.OmittedExpression) { // Assignment for target = value.propName should highligh whole property, hence use e as source map node if (e.kind !== SyntaxKind.SpreadElementExpression) { emitDestructuringAssignment(e, createElementAccess(value, createLiteral(i)), e); } - else if (i === elements.length - 1) { + else if (i === numElements - 1) { emitDestructuringAssignment((e).expression, createArraySlice(value, i), e); } } @@ -299,11 +307,11 @@ namespace ts { // so in that case, we'll intentionally create that temporary. value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0, target, emitTempVariableAssignment); } - for (let i = 0; i < elements.length; i++) { - let element = elements[i]; + for (let i = 0; i < numElements; i++) { + const element = elements[i]; if (name.kind === SyntaxKind.ObjectBindingPattern) { // Rewrite element to a declaration with an initializer that fetches property - let propName = element.propertyName || element.name; + const propName = element.propertyName || element.name; emitBindingElement(element, createDestructuringPropertyAccess(value, propName)); } else if (element.kind !== SyntaxKind.OmittedExpression) { @@ -311,7 +319,7 @@ namespace ts { // Rewrite element to a declaration that accesses array element at index i emitBindingElement(element, createElementAccess(value, i)); } - else if (i === elements.length - 1) { + else if (i === numElements - 1) { emitBindingElement(element, createArraySlice(value, i)); } } @@ -332,16 +340,23 @@ namespace ts { ); } - function createDestructuringPropertyAccess(object: Expression, propertyName: PropertyName): LeftHandSideExpression { + /** + * Creates either a PropertyAccessExpression or an ElementAccessExpression for the + * right-hand side of a transformed destructuring assignment. + * + * @param expression The right-hand expression that is the source of the property. + * @param propertyName The destructuring property name. + */ + function createDestructuringPropertyAccess(expression: Expression, propertyName: PropertyName): LeftHandSideExpression { if (isComputedPropertyName(propertyName)) { return createElementAccess( - object, - ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, propertyName, emitTempVariableAssignment) + expression, + ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, /*location*/ propertyName, emitTempVariableAssignment) ); } else if (isIdentifier(propertyName)) { return createPropertyAccess( - object, + expression, propertyName.text ); } @@ -349,7 +364,7 @@ namespace ts { // We create a synthetic copy of the identifier in order to avoid the rewriting that might // otherwise occur when the identifier is emitted. return createElementAccess( - object, + expression, cloneNode(propertyName) ); } diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts index e4d1e564cb8b3..c207940508121 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -5,8 +5,6 @@ namespace ts { // TODO(rbuckton): ES7->ES6 transformer export function transformES7(context: TransformationContext) { - const { hoistVariableDeclaration } = context; - return transformSourceFile; function transformSourceFile(node: SourceFile) { diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index e6572ee87097e..8c76e9843e77a 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -202,7 +202,7 @@ namespace ts { case SyntaxKind.Constructor: // TypeScript constructors are elided. The constructor of a class will be - // reordered to the start of the member list in `transformClassDeclaration`. + // transformed as part of `transformClassDeclaration`. return undefined; case SyntaxKind.ClassDeclaration: @@ -1964,7 +1964,7 @@ namespace ts { ) ); - const block = createBlock(statements); + const block = createBlock(statements, /*location*/ node.body); // Minor optimization, emit `_super` helper to capture `super` access in an arrow. // This step isn't needed if we eventually transform this to ES5. diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 446bddbff10cf..fa9905280727f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2792,7 +2792,7 @@ namespace ts { else if (kind === SyntaxKind.ConditionalExpression) { return isSimpleExpressionWorker((node).condition, depth + 1) && isSimpleExpressionWorker((node).whenTrue, depth + 1) - && isSimpleExpressionWorker((node).whenFalse, depth + 1) + && isSimpleExpressionWorker((node).whenFalse, depth + 1); } else if (kind === SyntaxKind.VoidExpression || kind === SyntaxKind.TypeOfExpression From 08036b7ef65dfbbe0634c4486282553c92bd6404 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 23 Feb 2016 16:26:04 -0800 Subject: [PATCH 17/43] Fixed linter warnings, added comments and minor cleanup. --- src/compiler/transformers/destructuring.ts | 2 +- src/compiler/transformers/ts.ts | 284 ++++++++++++++------- 2 files changed, 186 insertions(+), 100 deletions(-) diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 0ada8d177b2ed..442201a1f7d1e 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -197,7 +197,7 @@ namespace ts { } if (isBinaryExpression(root)) { - emitDestructuringAssignment(root.left, value, location) + emitDestructuringAssignment(root.left, value, location); } else { emitBindingElement(root, value); diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 8c76e9843e77a..d791dd1eb4cdb 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -8,7 +8,6 @@ namespace ts { export function transformTypeScript(context: TransformationContext) { const { - nodeHasGeneratedName, getGeneratedNameForNode, makeUniqueName, setNodeEmitFlags, @@ -20,23 +19,46 @@ namespace ts { const resolver = context.getEmitResolver(); const compilerOptions = context.getCompilerOptions(); const languageVersion = getEmitScriptTarget(compilerOptions); - const decoratedClassAliases: Map = {}; - const currentDecoratedClassAliases: Map = {}; + + // Save the previous transformation hooks. const previousExpressionSubstitution = context.expressionSubstitution; const previousOnBeforeEmitNode = context.onBeforeEmitNode; const previousOnAfterEmitNode = context.onAfterEmitNode; - context.enableExpressionSubstitution(SyntaxKind.Identifier); + + // Set new transformation hooks. context.expressionSubstitution = substituteExpression; context.onBeforeEmitNode = onBeforeEmitNode; context.onAfterEmitNode = onAfterEmitNode; - let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false; + // These variables contain state that changes as we descend into the tree. let currentSourceFile: SourceFile; let currentNamespace: ModuleDeclaration; let currentNamespaceLocalName: Identifier; let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; let currentParent: Node; let currentNode: Node; + + // These variables keep track of whether expression substitution has been enabled for + // specific edge cases. They are persisted between each SourceFile transformation and + // should not be reset. + let hasEnabledExpressionSubstitutionForDecoratedClasses = false; + let hasEnabledExpressionSubstitutionForNamespaceExports = false; + let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false; + + // This map keeps track of aliases created for classes with decorators to avoid issues + // with the double-binding behavior of classes. + let decoratedClassAliases: Map; + + // This map keeps track of currently active aliases defined in `decoratedClassAliases` + // when just-in-time substitution occurs while printing an expression identifier. + let currentDecoratedClassAliases: Map; + + // This value keeps track of how deeply nested we are within any containing namespaces + // when performing just-in-time substitution while printing an expression identifier. + let namespaceNestLevel: number; + + // This array keeps track of containers where `super` is valid, for use with + // just-in-time substitution for `super` expressions inside of async methods. let superContainerStack: SuperContainer[]; return transformSourceFile; @@ -54,16 +76,23 @@ namespace ts { * @param node The node to visit. */ function visitWithStack(node: Node, visitor: (node: Node) => Node): Node { + // Save state const savedCurrentNamespace = currentNamespace; const savedCurrentScope = currentScope; const savedCurrentParent = currentParent; const savedCurrentNode = currentNode; + + // Handle state changes before visiting a node. onBeforeVisitNode(node); + node = visitor(node); + + // Restore state currentNamespace = savedCurrentNamespace; currentScope = savedCurrentScope; currentParent = savedCurrentParent; currentNode = savedCurrentNode; + return node; } @@ -146,6 +175,11 @@ namespace ts { return visitorWorker(node); } + /** + * Branching visitor, visits a TypeScript syntax node. + * + * @param node The node to visit. + */ function visitTypeScript(node: Node): Node { // TypeScript ambient declarations are elided. if (node.flags & NodeFlags.Ambient) { @@ -461,11 +495,13 @@ namespace ts { // Record an alias to avoid class double-binding. if (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.ClassWithBodyScopedClassBinding) { + enableExpressionSubstitutionForDecoratedClasses(); decoratedClassAlias = makeUniqueName(node.name ? node.name.text : "default"); decoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAlias; // We emit the class alias as a `let` declaration here so that it has the same // TDZ as the class. + // let ${decoratedClassAlias}; addNode(statements, createVariableStatement( @@ -528,7 +564,7 @@ namespace ts { // Write any decorators of the node. addNodes(statements, generateClassElementDecorationStatements(node, /*isStatic*/ false)); addNodes(statements, generateClassElementDecorationStatements(node, /*isStatic*/ true)); - addNode(statements, generateConstructorDecorationStatement(node, decoratedClassAlias)) + addNode(statements, generateConstructorDecorationStatement(node, decoratedClassAlias)); // If the class is exported as part of a TypeScript namespace, emit the namespace export. // Otherwise, if the class was exported at the top level and was decorated, emit an export @@ -1060,7 +1096,7 @@ namespace ts { * Transforms all of the decorators for a declaration into an array of expressions. * * @param node The declaration node. - * @param allDecorators The AllDecorators object for the node. + * @param allDecorators An object containing all of the decorators for the declaration. */ function transformAllDecoratorsOfDeclaration(node: Declaration, allDecorators: AllDecorators) { if (!allDecorators) { @@ -1257,13 +1293,13 @@ namespace ts { */ function addTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) { if (compilerOptions.emitDecoratorMetadata) { - if (shouldAppendTypeMetadata(node)) { + if (shouldAddTypeMetadata(node)) { decoratorExpressions.push(createMetadataHelper("design:type", serializeTypeOfNode(node), /*defer*/ true)); } - if (shouldAppendParamTypesMetadata(node)) { + if (shouldAddParamTypesMetadata(node)) { decoratorExpressions.push(createMetadataHelper("design:paramtypes", serializeParameterTypesOfNode(node), /*defer*/ true)); } - if (shouldAppendReturnTypeMetadata(node)) { + if (shouldAddReturnTypeMetadata(node)) { decoratorExpressions.push(createMetadataHelper("design:returntype", serializeReturnTypeOfNode(node), /*defer*/ true)); } } @@ -1271,12 +1307,12 @@ namespace ts { /** * Determines whether to emit the "design:type" metadata based on the node's kind. - * The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata - * compiler option is set. + * The caller should have already tested whether the node has decorators and whether the + * emitDecoratorMetadata compiler option is set. * * @param node The node to test. */ - function shouldAppendTypeMetadata(node: Declaration): boolean { + function shouldAddTypeMetadata(node: Declaration): boolean { const kind = node.kind; return kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor @@ -1286,23 +1322,23 @@ namespace ts { /** * Determines whether to emit the "design:returntype" metadata based on the node's kind. - * The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata - * compiler option is set. + * The caller should have already tested whether the node has decorators and whether the + * emitDecoratorMetadata compiler option is set. * * @param node The node to test. */ - function shouldAppendReturnTypeMetadata(node: Declaration): boolean { + function shouldAddReturnTypeMetadata(node: Declaration): boolean { return node.kind === SyntaxKind.MethodDeclaration; } /** * Determines whether to emit the "design:paramtypes" metadata based on the node's kind. - * The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata - * compiler option is set. + * The caller should have already tested whether the node has decorators and whether the + * emitDecoratorMetadata compiler option is set. * * @param node The node to test. */ - function shouldAppendParamTypesMetadata(node: Declaration): boolean { + function shouldAddParamTypesMetadata(node: Declaration): boolean { const kind = node.kind; return kind === SyntaxKind.ClassDeclaration || kind === SyntaxKind.ClassExpression @@ -1432,7 +1468,7 @@ namespace ts { case SyntaxKind.TypePredicate: case SyntaxKind.BooleanKeyword: - return createIdentifier("Boolean") + return createIdentifier("Boolean"); case SyntaxKind.StringKeyword: case SyntaxKind.StringLiteral: @@ -1558,7 +1594,7 @@ namespace ts { * qualified name at runtime. */ function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): Expression { - let left: Expression + let left: Expression; if (node.left.kind === SyntaxKind.Identifier) { left = serializeEntityNameAsExpression(node.left, useFallback); } @@ -1990,25 +2026,6 @@ namespace ts { } } - function enableExpressionSubstitutionForAsyncMethodsWithSuper() { - if (!hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) { - hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = true; - - // We need to enable substitutions for call, property access, and element access - // if we need to rewrite super calls. - context.enableExpressionSubstitution(SyntaxKind.CallExpression); - context.enableExpressionSubstitution(SyntaxKind.PropertyAccessExpression); - context.enableExpressionSubstitution(SyntaxKind.ElementAccessExpression); - - // We need to be notified when entering and exiting declarations that bind super. - context.enableEmitNotification(SyntaxKind.ClassDeclaration); - context.enableEmitNotification(SyntaxKind.MethodDeclaration); - context.enableEmitNotification(SyntaxKind.GetAccessor); - context.enableEmitNotification(SyntaxKind.SetAccessor); - context.enableEmitNotification(SyntaxKind.Constructor); - } - } - /** * Visits a parameter declaration node. * @@ -2188,7 +2205,7 @@ namespace ts { * @param member The enum member node. */ function transformEnumMemberDeclarationValue(member: EnumMember): Expression { - let value = resolver.getConstantValue(member); + const value = resolver.getConstantValue(member); if (value !== undefined) { return createLiteral(value); } @@ -2274,6 +2291,8 @@ namespace ts { Debug.assert(isIdentifier(node.name)); + enableExpressionSubstitutionForNamespaceExports(); + const savedCurrentNamespaceLocalName = currentNamespaceLocalName; const modifiers = visitNodes(node.modifiers, visitor, isModifier); const statements: Statement[] = []; @@ -2471,19 +2490,6 @@ namespace ts { return createStatement(expression, /*location*/ undefined); } - function createExportStatement(node: ClassExpression | ClassDeclaration | FunctionDeclaration): Statement { - const name = getDeclarationName(node); - if (currentNamespace) { - return createNamespaceExport(name, name); - } - else if (node.flags & NodeFlags.Default) { - return createExportDefault(name); - } - else { - return createModuleExport(name); - } - } - function createNamespaceExport(exportName: Identifier, exportValue: Expression, location?: TextRange) { return createStatement( createAssignment( @@ -2506,7 +2512,7 @@ namespace ts { name = getSynthesizedNode(name); return currentNamespaceLocalName ? createPropertyAccess(currentNamespaceLocalName, name) - : name + : name; } function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) { @@ -2523,8 +2529,65 @@ namespace ts { : getClassPrototype(node); } + function onBeforeEmitNode(node: Node): void { + previousOnBeforeEmitNode(node); + + const kind = node.kind; + if (hasEnabledExpressionSubstitutionForDecoratedClasses + && kind === SyntaxKind.ClassDeclaration && node.decorators) { + currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)]; + } + + if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper + && (kind === SyntaxKind.ClassDeclaration + || kind === SyntaxKind.Constructor + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor)) { + + if (!superContainerStack) { + superContainerStack = []; + } + + superContainerStack.push(node); + } + + if (hasEnabledExpressionSubstitutionForNamespaceExports + && kind === SyntaxKind.ModuleDeclaration) { + namespaceNestLevel++; + } + } + + function onAfterEmitNode(node: Node): void { + previousOnAfterEmitNode(node); + + const kind = node.kind; + if (hasEnabledExpressionSubstitutionForDecoratedClasses + && kind === SyntaxKind.ClassDeclaration && node.decorators) { + currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined; + } + + if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper + && (kind === SyntaxKind.ClassDeclaration + || kind === SyntaxKind.Constructor + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor)) { + + if (superContainerStack) { + superContainerStack.pop(); + } + } + + if (hasEnabledExpressionSubstitutionForNamespaceExports + && kind === SyntaxKind.ModuleDeclaration) { + namespaceNestLevel--; + } + } + function substituteExpression(node: Expression): Expression { - node = previousExpressionSubstitution ? previousExpressionSubstitution(node) : node; + node = previousExpressionSubstitution(node); + switch (node.kind) { case SyntaxKind.Identifier: return substituteExpressionIdentifier(node); @@ -2544,8 +2607,10 @@ namespace ts { return node; } - function substituteExpressionIdentifier(node: Identifier) { - if (!nodeIsSynthesized(node) && resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) { + function substituteExpressionIdentifier(node: Identifier): Expression { + if (hasEnabledExpressionSubstitutionForDecoratedClasses + && !nodeIsSynthesized(node) + && resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) { // 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. @@ -2559,10 +2624,23 @@ namespace ts { } } + if (hasEnabledExpressionSubstitutionForNamespaceExports + && namespaceNestLevel > 0) { + // If we are nested within a namespace declaration, we may need to qualifiy + // an identifier that is exported from a merged namespace. + const original = getOriginalNode(node); + if (isIdentifier(original) && original.parent) { + const container = resolver.getReferencedExportContainer(original); + if (container && container.kind === SyntaxKind.ModuleDeclaration) { + return createPropertyAccess(getGeneratedNameForNode(container), node, /*location*/ node); + } + } + } + return node; } - function substituteCallExpression(node: CallExpression) { + function substituteCallExpression(node: CallExpression): Expression { const expression = node.expression; if (isSuperPropertyOrElementAccess(expression)) { const flags = getSuperContainerAsyncMethodFlags(); @@ -2612,6 +2690,54 @@ namespace ts { return node; } + function enableExpressionSubstitutionForAsyncMethodsWithSuper() { + if (!hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) { + hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = true; + + // We need to enable substitutions for call, property access, and element access + // if we need to rewrite super calls. + context.enableExpressionSubstitution(SyntaxKind.CallExpression); + context.enableExpressionSubstitution(SyntaxKind.PropertyAccessExpression); + context.enableExpressionSubstitution(SyntaxKind.ElementAccessExpression); + + // We need to be notified when entering and exiting declarations that bind super. + context.enableEmitNotification(SyntaxKind.ClassDeclaration); + context.enableEmitNotification(SyntaxKind.MethodDeclaration); + context.enableEmitNotification(SyntaxKind.GetAccessor); + context.enableEmitNotification(SyntaxKind.SetAccessor); + context.enableEmitNotification(SyntaxKind.Constructor); + } + } + + function enableExpressionSubstitutionForDecoratedClasses() { + if (!hasEnabledExpressionSubstitutionForDecoratedClasses) { + hasEnabledExpressionSubstitutionForDecoratedClasses = true; + + // We need to enable substitutions for identifiers. This allows us to + // substitute class names inside of a class declaration. + context.enableExpressionSubstitution(SyntaxKind.Identifier); + + // Keep track of class aliases. + decoratedClassAliases = {}; + currentDecoratedClassAliases = {}; + } + } + + function enableExpressionSubstitutionForNamespaceExports() { + if (!hasEnabledExpressionSubstitutionForNamespaceExports) { + hasEnabledExpressionSubstitutionForNamespaceExports = true; + + // We need to enable substitutions for identifiers. This allows us to + // substitute the names of exported members of a namespace. + context.enableExpressionSubstitution(SyntaxKind.Identifier); + + // We need to be notified when entering and exiting namespaces. + context.enableEmitNotification(SyntaxKind.ModuleDeclaration); + + // Keep track of namespace nesting depth + namespaceNestLevel = 0; + } + } function createSuperAccessInAsyncMethod(argumentExpression: Expression, flags: NodeCheckFlags, location: TextRange): LeftHandSideExpression { if (flags & NodeCheckFlags.AsyncMethodWithSuperBinding) { @@ -2638,45 +2764,5 @@ namespace ts { return container !== undefined && resolver.getNodeCheckFlags(getOriginalNode(container)) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding); } - - function onBeforeEmitNode(node: Node): void { - const kind = node.kind; - if (kind === SyntaxKind.ClassDeclaration && node.decorators) { - currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)]; - } - - if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper - && (kind === SyntaxKind.ClassDeclaration - || kind === SyntaxKind.Constructor - || kind === SyntaxKind.MethodDeclaration - || kind === SyntaxKind.GetAccessor - || kind === SyntaxKind.SetAccessor)) { - - if (!superContainerStack) { - superContainerStack = []; - } - - superContainerStack.push(node); - } - } - - function onAfterEmitNode(node: Node): void { - const kind = node.kind; - if (kind === SyntaxKind.ClassDeclaration && node.decorators) { - currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined; - } - - if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper - && (kind === SyntaxKind.ClassDeclaration - || kind === SyntaxKind.Constructor - || kind === SyntaxKind.MethodDeclaration - || kind === SyntaxKind.GetAccessor - || kind === SyntaxKind.SetAccessor)) { - - if (superContainerStack) { - superContainerStack.pop(); - } - } - } } } \ No newline at end of file From c9f52535f8ef2165ed390169b09cf914da0ca970 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 23 Feb 2016 16:36:02 -0800 Subject: [PATCH 18/43] Fixed linter warnings --- src/compiler/emitter.ts | 5 ----- src/compiler/factory.ts | 2 +- src/compiler/transformers/jsx.ts | 13 ++++--------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 47547ca94ac1b..9e74572b60dde 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -288,11 +288,6 @@ namespace ts { } export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { - return printFiles(resolver, host, targetSourceFile); - } - - // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature - export function legacyEmitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { // emit output for the __extends helper function const extendsHelper = ` var __extends = (this && this.__extends) || function (d, b) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 0720906830f7b..8f388a01520b6 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -743,7 +743,7 @@ namespace ts { export function createJsxCreateElement(reactNamespace: string, tagName: Expression, props: Expression, children: Expression[]): LeftHandSideExpression { const argumentsList = [tagName]; if (props) { - argumentsList.push(props) + argumentsList.push(props); } if (children && children.length > 0) { diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 00df9c8abdcd7..cb1aef32a9799 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -40,7 +40,7 @@ namespace ts { function transformJsxChildToExpression(node: JsxChild): Expression { switch (node.kind) { case SyntaxKind.JsxText: - return visitNonEmptyJsxText(node); + return visitJsxText(node); case SyntaxKind.JsxExpression: return visitJsxExpression(node); @@ -69,7 +69,7 @@ namespace ts { let objectProperties: Expression; if (node.attributes.length === 0) { // When there are no attributes, React wants "null" - objectProperties = createLiteral(null); + objectProperties = createNull(); } else { // Either emit one big object literal (no spread attribs), or @@ -124,7 +124,7 @@ namespace ts { return createPropertyAssignment(name, expression); } - function visitNonEmptyJsxText(node: JsxText) { + function visitJsxText(node: JsxText) { const text = getTextToEmit(node); if (text !== undefined) { return createLiteral(text); @@ -155,7 +155,7 @@ namespace ts { const c = text.charCodeAt(i); if (isLineBreak(c)) { if (firstNonWhitespace !== -1 && (lastNonWhitespace - firstNonWhitespace + 1 > 0)) { - let part = text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1); + const part = text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1); result = (result ? result + "\" + ' ' + \"" : "") + part; } firstNonWhitespace = -1; @@ -218,11 +218,6 @@ namespace ts { } } - function visitJsxText(node: JsxText) { - const text = trimReactWhitespaceAndApplyEntities(node); - return createLiteral(text || ""); - } - function visitJsxExpression(node: JsxExpression) { return visitNode(node.expression, visitor, isExpression); } From 25f41027c73986d83f9c9dd5b8f68d90ab484f0a Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 23 Feb 2016 16:36:50 -0800 Subject: [PATCH 19/43] Reverted change to emitter to force use of printFiles --- src/compiler/emitter.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 47547ca94ac1b..357fec7f0fd65 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -287,12 +287,8 @@ namespace ts { _i = 0x10000000, // Use/preference flag for '_i' } - export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { - return printFiles(resolver, host, targetSourceFile); - } - // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature - export function legacyEmitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { + export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { // emit output for the __extends helper function const extendsHelper = ` var __extends = (this && this.__extends) || function (d, b) { From 72bfd2f5b6a8467c52a4e99e2fc27bab04cc532c Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 23 Feb 2016 17:06:54 -0800 Subject: [PATCH 20/43] Fixed linter warnings. --- src/compiler/transformers/module/module.ts | 2 +- src/compiler/transformers/module/system.ts | 42 +++++++++------------- src/compiler/utilities.ts | 4 +-- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 5ef6a0d182566..68154443ce953 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -338,7 +338,7 @@ namespace ts { function visitExportDeclaration(node: ExportDeclaration): OneOrMany { if (contains(externalImports, node)) { - let generatedName = getGeneratedNameForNode(node); + const generatedName = getGeneratedNameForNode(node); if (node.exportClause) { const statements: Statement[] = []; // export { x, y } from "mod"; diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 9ffc613ae0c86..bf692936800b7 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -37,7 +37,7 @@ namespace ts { let contextObjectForFile: Identifier; let exportedLocalNames: Identifier[]; let exportedFunctionDeclarations: ExpressionStatement[]; - let noSubstitution: Map = {}; + const noSubstitution: Map = {}; return transformSourceFile; @@ -992,7 +992,7 @@ namespace ts { function hasExportedReferenceInArrayDestructuringElement(node: Expression): boolean { if (isSpreadElementExpression(node)) { - let expression = node.expression; + const expression = node.expression; return isIdentifier(expression) && isExportedBinding(expression); } else { @@ -1002,7 +1002,7 @@ namespace ts { function hasExportedReferenceInDestructuringElement(node: Expression): boolean { if (isBinaryExpression(node)) { - let left = node.left; + const left = node.left; return node.operatorToken.kind === SyntaxKind.EqualsToken && isDestructuringPattern(left) && hasExportedReferenceInDestructuringPattern(left); @@ -1011,7 +1011,7 @@ namespace ts { return isExportedBinding(node); } else if (isSpreadElementExpression(node)) { - let expression = node.expression; + const expression = node.expression; return isIdentifier(expression) && isExportedBinding(expression); } else if (isDestructuringPattern(node)) { @@ -1052,7 +1052,7 @@ namespace ts { } function getExternalModuleNameLiteral(importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration) { - let moduleName = getExternalModuleName(importNode); + const moduleName = getExternalModuleName(importNode); if (moduleName.kind === SyntaxKind.StringLiteral) { return tryRenameExternalModule(moduleName) || getSynthesizedNode(moduleName); @@ -1074,12 +1074,12 @@ namespace ts { } function getLocalNameTextForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): string { - let name = getLocalNameForExternalImport(node); + const name = getLocalNameForExternalImport(node); return name ? name.text : undefined; } function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): Identifier { - let namespaceDeclaration = getNamespaceDeclarationNode(node); + const namespaceDeclaration = getNamespaceDeclarationNode(node); if (namespaceDeclaration && !isDefaultImport(node)) { return createIdentifier(getSourceTextOfNodeFromSourceFile(currentSourceFile, namespaceDeclaration.name)); } @@ -1182,8 +1182,8 @@ namespace ts { * @param node The declaration to export. */ function createDeclarationExport(node: DeclarationStatement) { - let declarationName = getDeclarationName(node); - let exportName = node.flags & NodeFlags.Default ? createLiteral("default") : declarationName; + const declarationName = getDeclarationName(node); + const exportName = node.flags & NodeFlags.Default ? createLiteral("default") : declarationName; return createExportStatement(exportName, declarationName); } @@ -1209,15 +1209,15 @@ namespace ts { } function collectDependencyGroups(externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]) { - let groupIndices: Map = {}; - let dependencyGroups: DependencyGroup[] = []; - for (let i = 0; i < externalImports.length; ++i) { - let externalImport = externalImports[i]; - let externalModuleName = getExternalModuleNameLiteral(externalImport); - let text = externalModuleName.text; + 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 - let groupIndex = groupIndices[text]; + const groupIndex = groupIndices[text]; dependencyGroups[groupIndex].externalImports.push(externalImport); continue; } @@ -1245,14 +1245,6 @@ namespace ts { exportedLocalNames.push(name); } - function hoistExportedVariableDeclaration(name: Identifier) { - - } - - function hoistExportedFunctionDeclaration(node: FunctionDeclaration) { - - } - function recordExportedFunctionDeclaration(node: FunctionDeclaration) { if (!exportedFunctionDeclarations) { exportedFunctionDeclarations = []; @@ -1262,7 +1254,7 @@ namespace ts { } function hoistBindingElement(node: VariableDeclaration | BindingElement, isExported: boolean) { - let name = node.name; + const name = node.name; if (isIdentifier(name)) { hoistVariableDeclaration(getSynthesizedNode(name)); if (isExported) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 62383f3cd3947..86f1c2a2b73bd 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2869,7 +2869,7 @@ namespace ts { const exportSpecifiers: Map = {}; let exportEquals: ExportAssignment = undefined; let hasExportStars = false; - for (let node of sourceFile.statements) { + for (const node of sourceFile.statements) { switch (node.kind) { case SyntaxKind.ImportDeclaration: if (!(node).importClause || @@ -2928,7 +2928,7 @@ namespace ts { } export function copyPrologueDirectives(from: Statement[], to: Statement[]): number { - for (let i = 0; i < from.length; ++i) { + for (let i = 0; i < from.length; i++) { if (isPrologueDirective(from[i])) { addNode(to, from[i]); } From cd2cf7d3c6ac93a5c14ff83c75387f149bdbe1a5 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 29 Feb 2016 13:28:16 -0800 Subject: [PATCH 21/43] PR Feedback and defer makeUniqueName/getGeneratedNameForNode to printer. --- src/compiler/factory.ts | 34 +++++--- src/compiler/printer.ts | 162 +++++++++++++++++++++++++++--------- src/compiler/transformer.ts | 118 -------------------------- src/compiler/types.ts | 17 ++-- src/compiler/utilities.ts | 12 +-- 5 files changed, 161 insertions(+), 182 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 8f388a01520b6..eae769fd562ab 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -185,24 +185,38 @@ namespace ts { // Identifiers - export function createIdentifier(text: string): Identifier { - const node = createNode(SyntaxKind.Identifier); + export function createIdentifier(text: string, location?: TextRange): Identifier { + const node = createNode(SyntaxKind.Identifier, location); node.text = text; return node; } - export function createTempVariable(): Identifier { - const name = createNode(SyntaxKind.Identifier); - name.text = undefined; - name.tempKind = TempVariableKind.Auto; + export function createTempVariable(location?: TextRange): Identifier { + const name = createNode(SyntaxKind.Identifier, location); + name.autoGenerateKind = GeneratedIdentifierKind.Auto; getNodeId(name); return name; } - export function createLoopVariable(): Identifier { - const name = createNode(SyntaxKind.Identifier); - name.text = undefined; - name.tempKind = TempVariableKind.Loop; + export function createLoopVariable(location?: TextRange): Identifier { + const name = createNode(SyntaxKind.Identifier, location); + name.autoGenerateKind = GeneratedIdentifierKind.Loop; + getNodeId(name); + return name; + } + + export function createUniqueName(text: string, location?: TextRange): Identifier { + const name = createNode(SyntaxKind.Identifier, location); + name.text = text; + name.autoGenerateKind = GeneratedIdentifierKind.Unique; + getNodeId(name); + return name; + } + + export function createGeneratedNameForNode(node: Node, location?: TextRange): Identifier { + const name = createNode(SyntaxKind.Identifier, location); + name.autoGenerateKind = GeneratedIdentifierKind.Node; + name.original = node; getNodeId(name); return name; } diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index a1d284dd42bba..e44e88b46128d 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -121,7 +121,6 @@ const _super = (function (geti, seti) { const writer = createTextWriter(newLine); const { write, - writeTextOfNode, writeLine, increaseIndent, decreaseIndent @@ -154,11 +153,12 @@ const _super = (function (geti, seti) { let identifierSubstitution: (node: Identifier) => Identifier; let onBeforeEmitNode: (node: Node) => void; let onAfterEmitNode: (node: Node) => void; - let isUniqueName: (name: string) => boolean; - let temporaryVariables: string[] = []; + let nodeToGeneratedName: string[]; + let generatedNameSet: Map; let tempFlags: TempFlags; let currentSourceFile: SourceFile; let currentText: string; + let currentFileIdentifiers: Map; let extendsEmitted: boolean; let decorateEmitted: boolean; let paramEmitted: boolean; @@ -169,6 +169,8 @@ const _super = (function (geti, seti) { function doPrint(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); + nodeToGeneratedName = []; + generatedNameSet = {}; isOwnFileEmit = !isBundledEmit; // Emit helpers from all the files @@ -213,8 +215,6 @@ const _super = (function (geti, seti) { identifierSubstitution = undefined; onBeforeEmitNode = undefined; onAfterEmitNode = undefined; - isUniqueName = undefined; - temporaryVariables = undefined; tempFlags = TempFlags.Auto; currentSourceFile = undefined; currentText = undefined; @@ -236,13 +236,13 @@ const _super = (function (geti, seti) { identifierSubstitution = context.identifierSubstitution; onBeforeEmitNode = context.onBeforeEmitNode; onAfterEmitNode = context.onAfterEmitNode; - isUniqueName = context.isUniqueName; return printSourceFile; } function printSourceFile(node: SourceFile) { currentSourceFile = node; currentText = node.text; + currentFileIdentifiers = node.identifiers; sourceMap.setSourceFile(node); comments.setSourceFile(node); emitWorker(node); @@ -659,22 +659,11 @@ const _super = (function (geti, seti) { // function emitIdentifier(node: Identifier) { - if (node.text === undefined) { - // Emit a temporary variable name for this node. - const nodeId = getOriginalNodeId(node); - const text = temporaryVariables[nodeId] || (temporaryVariables[nodeId] = makeTempVariableName(tempKindToFlags(node.tempKind))); - write(text); - } - else if (nodeIsSynthesized(node) || !node.parent) { - if (getNodeEmitFlags(node) & NodeEmitFlags.UMDDefine) { - writeLines(umdHelper); - } - else { - write(node.text); - } + if (getNodeEmitFlags(node) && NodeEmitFlags.UMDDefine) { + writeLines(umdHelper); } else { - writeTextOfNode(currentText, node); + write(getTextOfNode(node, /*includeTrivia*/ false)); } } @@ -1720,7 +1709,6 @@ const _super = (function (geti, seti) { emitExpression(node.expression); write(":"); - debugger; emitCaseOrDefaultClauseStatements(node, node.statements); } @@ -1763,14 +1751,14 @@ const _super = (function (geti, seti) { function emitPropertyAssignment(node: PropertyAssignment) { emit(node.name); write(": "); - // // This is to ensure that we emit comment in the following case: - // // For example: - // // obj = { - // // id: /*comment1*/ ()=>void - // // } - // // "comment1" is not considered to be leading comment for node.initializer - // // but rather a trailing comment on the previous node. - // emitTrailingCommentsOfPosition(node.initializer.pos); + // This is to ensure that we emit comment in the following case: + // For example: + // obj = { + // id: /*comment1*/ ()=>void + // } + // "comment1" is not considered to be leading comment for node.initializer + // but rather a trailing comment on the previous node. + emitLeadingComments(node.initializer, getTrailingComments(collapseTextRange(node.initializer, TextRangeCollapse.CollapseToStart))); emitExpression(node.initializer); } @@ -1951,11 +1939,8 @@ const _super = (function (geti, seti) { } function emitModifiers(node: Node, modifiers: ModifiersArray) { - const startingPos = writer.getTextPos(); - emitList(node, modifiers, ListFormat.SingleLine); - - const endingPos = writer.getTextPos(); - if (startingPos !== endingPos) { + if (modifiers && modifiers.length) { + emitList(node, modifiers, ListFormat.SingleLine); write(" "); } } @@ -2345,7 +2330,15 @@ const _super = (function (geti, seti) { } function getTextOfNode(node: Node, includeTrivia?: boolean) { - if (nodeIsSynthesized(node) && (isLiteralExpression(node) || isIdentifier(node))) { + if (isIdentifier(node)) { + if (node.autoGenerateKind) { + return getGeneratedIdentifier(node); + } + else if (nodeIsSynthesized(node) || !node.parent) { + return node.text; + } + } + else if (isLiteralExpression(node) && (nodeIsSynthesized(node) || !node.parent)) { return node.text; } @@ -2368,10 +2361,22 @@ const _super = (function (geti, seti) { && rangeEndIsOnSameLineAsRangeStart(block, block); } - function tempKindToFlags(kind: TempVariableKind) { - return kind === TempVariableKind.Loop - ? TempFlags._i - : TempFlags.Auto; + function isUniqueName(name: string): boolean { + return !resolver.hasGlobalName(name) && + !hasProperty(currentFileIdentifiers, name) && + !hasProperty(generatedNameSet, name); + } + + function isUniqueLocalName(name: string, container: Node): boolean { + for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) { + if (node.locals && hasProperty(node.locals, name)) { + // We conservatively include alias symbols to cover cases where they're emitted as locals + if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { + return false; + } + } + } + return true; } /** @@ -2401,6 +2406,85 @@ const _super = (function (geti, seti) { } } } + + // Generate a name that is unique within the current file and doesn't conflict with any names + // in global scope. The name is formed by adding an '_n' suffix to the specified base name, + // where n is a positive integer. Note that names generated by makeTempVariableName and + // makeUniqueName are guaranteed to never conflict. + function makeUniqueName(baseName: string): string { + // Find the first unique 'name_n', where n is a positive number + if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) { + baseName += "_"; + } + let i = 1; + while (true) { + const generatedName = baseName + i; + if (isUniqueName(generatedName)) { + return generatedNameSet[generatedName] = generatedName; + } + i++; + } + } + + function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) { + const name = node.name.text; + // Use module/enum name itself if it is unique, otherwise make a unique variation + return isUniqueLocalName(name, node) ? name : makeUniqueName(name); + } + + function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { + const expr = getExternalModuleName(node); + const baseName = expr.kind === SyntaxKind.StringLiteral ? + escapeIdentifier(makeIdentifierFromModuleName((expr).text)) : "module"; + return makeUniqueName(baseName); + } + + function generateNameForExportDefault() { + return makeUniqueName("default"); + } + + function generateNameForClassExpression() { + return makeUniqueName("class"); + } + + function generateNameForNode(node: Node) { + switch (node.kind) { + case SyntaxKind.Identifier: + return makeUniqueName((node).text); + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.EnumDeclaration: + return generateNameForModuleOrEnum(node); + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportDeclaration: + return generateNameForImportOrExportDeclaration(node); + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ExportAssignment: + return generateNameForExportDefault(); + case SyntaxKind.ClassExpression: + return generateNameForClassExpression(); + default: + return makeTempVariableName(TempFlags.Auto); + } + } + + function generateIdentifier(node: Identifier) { + switch (node.autoGenerateKind) { + case GeneratedIdentifierKind.Auto: + return makeTempVariableName(TempFlags.Auto); + case GeneratedIdentifierKind.Loop: + return makeTempVariableName(TempFlags._i); + case GeneratedIdentifierKind.Unique: + return makeUniqueName(node.text); + case GeneratedIdentifierKind.Node: + return generateNameForNode(getOriginalNode(node)); + } + } + + function getGeneratedIdentifier(node: Identifier) { + const id = getOriginalNodeId(node); + return nodeToGeneratedName[id] || (nodeToGeneratedName[id] = unescapeIdentifier(generateIdentifier(node))); + } } } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 65d61c044c22a..98b71f0c16e40 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -22,8 +22,6 @@ namespace ts { * @param transforms An array of Transformers. */ export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]) { - const nodeToGeneratedName: Identifier[] = []; - const generatedNameSet: Map = {}; const nodeEmitFlags: NodeEmitFlags[] = []; const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; @@ -40,10 +38,6 @@ namespace ts { getEmitResolver: () => resolver, getNodeEmitFlags, setNodeEmitFlags, - isUniqueName, - getGeneratedNameForNode, - nodeHasGeneratedName, - makeUniqueName, hoistVariableDeclaration, hoistFunctionDeclaration, startLexicalEnvironment, @@ -119,118 +113,6 @@ namespace ts { return node; } - /** - * Generate a name that is unique within the current file and doesn't conflict with any names - * in global scope. The name is formed by adding an '_n' suffix to the specified base name, - * where n is a positive integer. Note that names generated by makeTempVariableName and - * makeUniqueName are guaranteed to never conflict. - */ - function makeUniqueName(baseName: string): Identifier { - // Find the first unique 'name_n', where n is a positive number - if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) { - baseName += "_"; - } - - let i = 1; - while (true) { - const generatedName = baseName + i; - if (isUniqueName(generatedName)) { - return createIdentifier(generatedNameSet[generatedName] = generatedName); - } - - i++; - } - } - - /** - * Gets the generated name for a node. - */ - function getGeneratedNameForNode(node: Node) { - const id = getNodeId(node); - return nodeToGeneratedName[id] || (nodeToGeneratedName[id] = generateNameForNode(node)); - } - - /** - * Gets a value indicating whether a node has a generated name. - */ - function nodeHasGeneratedName(node: Node) { - const id = getNodeId(node); - return nodeToGeneratedName[id] !== undefined; - } - - /** - * Tests whether the provided name is unique. - */ - function isUniqueName(name: string): boolean { - return !resolver.hasGlobalName(name) - && !hasProperty(currentSourceFile.identifiers, name) - && !hasProperty(generatedNameSet, name); - } - - /** - * Tests whether the provided name is unique within a container. - */ - function isUniqueLocalName(name: string, container: Node): boolean { - container = getOriginalNode(container); - for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) { - if (node.locals && hasProperty(node.locals, name)) { - // We conservatively include alias symbols to cover cases where they're emitted as locals - if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { - return false; - } - } - } - return true; - } - - /** - * Generates a name for a node. - */ - function generateNameForNode(node: Node): Identifier { - switch (node.kind) { - case SyntaxKind.Identifier: - return makeUniqueName((node).text); - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.EnumDeclaration: - return generateNameForModuleOrEnum(node); - case SyntaxKind.ImportDeclaration: - case SyntaxKind.ExportDeclaration: - return generateNameForImportOrExportDeclaration(node); - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ClassDeclaration: - Debug.assert((node.flags & NodeFlags.Default) !== 0, "Can only generate a name for a default export."); - return generateNameForExportDefault(); - case SyntaxKind.ExportAssignment: - return generateNameForExportDefault(); - case SyntaxKind.ClassExpression: - return generateNameForClassExpression(); - default: - return createTempVariable(); - } - } - - function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) { - const name = node.name; - // Use module/enum name itself if it is unique, otherwise make a unique variation - return isUniqueLocalName(name.text, node) ? name : makeUniqueName(name.text); - } - - function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { - const expr = getExternalModuleName(node); - const baseName = expr.kind === SyntaxKind.StringLiteral - ? escapeIdentifier(makeIdentifierFromModuleName((expr).text)) - : "module"; - return makeUniqueName(baseName); - } - - function generateNameForExportDefault() { - return makeUniqueName("default"); - } - - function generateNameForClassExpression() { - return makeUniqueName("class"); - } - /** * Records a hoisted variable declaration for the provided name within a lexical environment. */ diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6a55153f63141..fb5ab0c36091e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -497,16 +497,19 @@ namespace ts { // @kind(SyntaxKind.StaticKeyword) export interface Modifier extends Node { } - export const enum TempVariableKind { - Auto, // Automatically generated identifier - Loop, // Automatically generated identifier with a preference for '_i' + export const enum GeneratedIdentifierKind { + None, // Not automatically generated. + Auto, // Automatically generated identifier. + Loop, // Automatically generated identifier with a preference for '_i'. + Unique, // Unique name based on the 'text' property. + Node, // Unique name based on the node in the 'original' property. } // @kind(SyntaxKind.Identifier) export interface Identifier extends PrimaryExpression { text: string; // Text of identifier (with escapes converted to characters) - tempKind?: TempVariableKind; // Specifies whether to auto-generate the text for an identifier. originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later + autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier. } // @kind(SyntaxKind.QualifiedName) @@ -2805,7 +2808,7 @@ namespace ts { SingleLine = 1 << 6, // The contents of this node should be emit on a single line. 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 << 8, // Emits comments of missing parent nodes. + EmitCommentsOfNotEmittedParent = 1 << 9, // Emits comments of missing parent nodes. } /** Additional context provided to `visitEachChild` */ @@ -2825,10 +2828,6 @@ namespace ts { setNodeEmitFlags(node: T, flags: NodeEmitFlags): T; hoistFunctionDeclaration(node: FunctionDeclaration): void; hoistVariableDeclaration(node: Identifier): void; - isUniqueName(name: string): boolean; - getGeneratedNameForNode(node: Node): Identifier; - nodeHasGeneratedName(node: Node): boolean; - makeUniqueName(baseName: string): Identifier; /** * Hook used by transformers to substitute non-expression identifiers diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 10b5b467158f0..514ee5f258da8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2297,11 +2297,11 @@ namespace ts { writer.write(" "); } - let emitInterveningSeperator = false; + let emitInterveningSeparator = false; for (const comment of comments) { - if (emitInterveningSeperator) { + if (emitInterveningSeparator) { writer.write(" "); - emitInterveningSeperator = false; + emitInterveningSeparator = false; } writeComment(text, lineMap, writer, comment, newLine); @@ -2309,11 +2309,11 @@ namespace ts { writer.writeLine(); } else { - emitInterveningSeperator = true; + emitInterveningSeparator = true; } } - if (emitInterveningSeperator && trailingSeparator) { + if (emitInterveningSeparator && trailingSeparator) { writer.write(" "); } } @@ -2723,7 +2723,7 @@ namespace ts { } return collapse === TextRangeCollapse.CollapseToStart - ? { pos: range.pos, end: range.end } + ? { pos: range.pos, end: range.pos } : { pos: range.end, end: range.end }; } From b1d88282ce4d56a79ae0f2aa5d53e392cb01e556 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 29 Feb 2016 13:29:03 -0800 Subject: [PATCH 22/43] Remove transformCompatibleEmit flag and make it the default. --- Jakefile.js | 8 - src/compiler/commandLineParser.ts | 7 - src/compiler/emitter.ts | 27 +- src/compiler/types.ts | 1 - ...ticVariableAndExportedVarThatShareAName.js | 4 +- ...VariableAndNonExportedVarThatShareAName.js | 4 +- ...onWithInvalidConstOnPropertyDeclaration.js | 2 +- .../baselines/reference/ES5SymbolProperty1.js | 3 +- .../amdImportNotAsPrimaryExpression.js | 2 +- tests/baselines/reference/autolift4.js | 2 +- .../blockScopedVariablesUseBeforeDef.js | 30 +- tests/baselines/reference/class2.js | 2 +- .../classExpressionWithDecorator1.js | 8 +- .../classExpressionWithStaticProperties1.js | 16 +- .../classExpressionWithStaticProperties2.js | 14 +- .../classMemberInitializerScoping.js | 2 +- .../classMemberInitializerWithLamdaScoping.js | 14 +- .../reference/classWithPrivateProperty.js | 2 +- .../reference/classWithProtectedProperty.js | 2 +- .../reference/classWithPublicProperty.js | 2 +- .../reference/cloduleStaticMembers.js | 4 +- .../reference/commentsOnStaticMembers.js | 16 +- .../commonJSImportAsPrimaryExpression.js | 2 +- .../commonJSImportNotAsPrimaryExpression.js | 2 +- .../reference/computedPropertyNames10_ES5.js | 3 +- .../reference/computedPropertyNames11_ES5.js | 3 +- .../reference/computedPropertyNames12_ES5.js | 2 +- .../reference/computedPropertyNames18_ES5.js | 3 +- .../reference/computedPropertyNames19_ES5.js | 3 +- .../reference/computedPropertyNames1_ES5.js | 3 +- .../reference/computedPropertyNames20_ES5.js | 3 +- .../reference/computedPropertyNames22_ES5.js | 3 +- .../reference/computedPropertyNames25_ES5.js | 3 +- .../reference/computedPropertyNames28_ES5.js | 3 +- .../reference/computedPropertyNames29_ES5.js | 3 +- .../reference/computedPropertyNames30_ES5.js | 3 +- .../reference/computedPropertyNames31_ES5.js | 3 +- .../reference/computedPropertyNames33_ES5.js | 3 +- .../reference/computedPropertyNames34_ES5.js | 3 +- .../reference/computedPropertyNames46_ES5.js | 3 +- .../reference/computedPropertyNames47_ES5.js | 3 +- .../reference/computedPropertyNames48_ES5.js | 9 +- .../reference/computedPropertyNames49_ES5.js | 3 +- .../reference/computedPropertyNames4_ES5.js | 3 +- .../reference/computedPropertyNames50_ES5.js | 3 +- .../reference/computedPropertyNames5_ES5.js | 3 +- .../reference/computedPropertyNames6_ES5.js | 3 +- .../reference/computedPropertyNames7_ES5.js | 3 +- .../reference/computedPropertyNames8_ES5.js | 3 +- .../reference/computedPropertyNames9_ES5.js | 3 +- ...mputedPropertyNamesContextualType10_ES5.js | 3 +- ...omputedPropertyNamesContextualType1_ES5.js | 3 +- ...omputedPropertyNamesContextualType2_ES5.js | 3 +- ...omputedPropertyNamesContextualType3_ES5.js | 3 +- ...omputedPropertyNamesContextualType4_ES5.js | 3 +- ...omputedPropertyNamesContextualType5_ES5.js | 3 +- ...omputedPropertyNamesContextualType6_ES5.js | 3 +- ...omputedPropertyNamesContextualType7_ES5.js | 3 +- ...omputedPropertyNamesContextualType8_ES5.js | 3 +- ...omputedPropertyNamesContextualType9_ES5.js | 3 +- ...mputedPropertyNamesDeclarationEmit5_ES5.js | 3 +- .../computedPropertyNamesSourceMap2_ES5.js | 3 +- ...computedPropertyNamesSourceMap2_ES5.js.map | 2 +- ...dPropertyNamesSourceMap2_ES5.sourcemap.txt | 16 +- ...TypedClassExpressionMethodDeclaration01.js | 40 +- .../reference/declFilePrivateStatic.js | 4 +- .../reference/decoratorCallGeneric.js | 6 +- .../decoratorChecksFunctionBodies.js | 14 +- ...ratorInstantiateModulesInFunctionBodies.js | 6 +- .../baselines/reference/decoratorMetadata.js | 20 +- ...taForMethodWithNoReturnTypeAnnotation01.js | 12 +- .../decoratorMetadataOnInferredType.js | 8 +- .../decoratorMetadataWithConstructorType.js | 8 +- ...adataWithImportDeclarationNameCollision.js | 8 +- ...dataWithImportDeclarationNameCollision2.js | 8 +- ...dataWithImportDeclarationNameCollision3.js | 8 +- ...dataWithImportDeclarationNameCollision4.js | 8 +- ...dataWithImportDeclarationNameCollision5.js | 8 +- ...dataWithImportDeclarationNameCollision6.js | 8 +- ...dataWithImportDeclarationNameCollision7.js | 8 +- ...dataWithImportDeclarationNameCollision8.js | 8 +- .../baselines/reference/decoratorOnClass1.js | 6 +- .../baselines/reference/decoratorOnClass2.js | 6 +- .../baselines/reference/decoratorOnClass3.js | 6 +- .../baselines/reference/decoratorOnClass4.js | 6 +- .../baselines/reference/decoratorOnClass5.js | 6 +- .../baselines/reference/decoratorOnClass8.js | 6 +- .../reference/decoratorOnClassAccessor1.js | 6 +- .../reference/decoratorOnClassAccessor2.js | 6 +- .../reference/decoratorOnClassAccessor3.js | 6 +- .../reference/decoratorOnClassAccessor4.js | 6 +- .../reference/decoratorOnClassAccessor5.js | 6 +- .../reference/decoratorOnClassAccessor6.js | 6 +- .../decoratorOnClassConstructorParameter1.js | 6 +- .../decoratorOnClassConstructorParameter4.js | 6 +- .../reference/decoratorOnClassMethod1.js | 6 +- .../reference/decoratorOnClassMethod10.js | 6 +- .../reference/decoratorOnClassMethod11.js | 6 +- .../reference/decoratorOnClassMethod12.js | 6 +- .../reference/decoratorOnClassMethod2.js | 6 +- .../reference/decoratorOnClassMethod3.js | 6 +- .../reference/decoratorOnClassMethod8.js | 6 +- .../decoratorOnClassMethodOverload2.js | 6 +- .../decoratorOnClassMethodParameter1.js | 6 +- .../reference/decoratorOnClassProperty1.js | 6 +- .../reference/decoratorOnClassProperty10.js | 6 +- .../reference/decoratorOnClassProperty11.js | 6 +- .../reference/decoratorOnClassProperty2.js | 6 +- .../reference/decoratorOnClassProperty3.js | 6 +- .../reference/decoratorOnClassProperty6.js | 6 +- .../reference/decoratorOnClassProperty7.js | 6 +- ...dClassSuperCallsInNonConstructorMembers.js | 2 +- tests/baselines/reference/errorSuperCalls.js | 4 +- .../reference/errorSuperPropertyAccess.js | 4 +- .../reference/es3defaultAliasIsQuoted.js | 2 +- tests/baselines/reference/es6ClassTest.js | 2 +- tests/baselines/reference/es6ClassTest2.js | 2 +- .../reference/generatedContextualTyping.js | 72 +- ...nericClassWithStaticsUsingTypeArguments.js | 8 +- .../baselines/reference/gettersAndSetters.js | 2 +- .../reference/importImportOnlyModule.js | 2 +- .../instanceAndStaticDeclarations1.js | 2 +- .../baselines/reference/invalidStaticField.js | 2 +- .../literalsInComputedProperties1.js | 3 +- .../reference/missingDecoratorType.js | 6 +- ...difierOnClassExpressionMemberInFunction.js | 18 +- tests/baselines/reference/noEmitHelpers2.js | 10 +- .../parserAccessibilityAfterStatic3.js | 2 +- ...ErrorRecovery_IncompleteMemberVariable1.js | 4 +- ...ErrorRecovery_IncompleteMemberVariable2.js | 4 +- tests/baselines/reference/parserharness.js | 6 +- .../privacyCannotNameVarTypeDeclFile.js | 24 +- tests/baselines/reference/privateIndexer2.js | 3 +- .../privateStaticMemberAccessibility.js | 2 +- .../reference/propertyAccessibility2.js | 2 +- .../reference/quotedPropertyName2.js | 2 +- .../baselines/reference/reassignStaticProp.js | 2 +- .../reference/sourceMap-FileWithComments.js | 4 +- .../sourceMap-FileWithComments.js.map | 2 +- .../sourceMap-FileWithComments.sourcemap.txt | 133 ++-- .../sourceMapValidationDecorators.js | 62 +- .../sourceMapValidationDecorators.js.map | 2 +- ...ourceMapValidationDecorators.sourcemap.txt | 676 +++++++++--------- tests/baselines/reference/staticClassProps.js | 2 +- .../staticMemberAccessOffDerivedType1.js | 2 +- .../reference/staticMemberInitialization.js | 2 +- .../staticMemberWithStringAndNumberNames.js | 10 +- .../reference/staticModifierAlreadySeen.js | 2 +- tests/baselines/reference/staticPropSuper.js | 2 +- tests/baselines/reference/statics.js | 6 +- .../reference/staticsInConstructorBodies.js | 2 +- .../reference/staticsNotInScopeInClodule.js | 2 +- .../reference/strictModeInConstructor.js | 6 +- tests/baselines/reference/superAccess.js | 2 +- tests/baselines/reference/superAccess2.js | 2 +- ...thisInArrowFunctionInStaticInitializer1.js | 8 +- .../reference/thisInConstructorParameter2.js | 2 +- .../reference/thisInInvalidContexts.js | 2 +- .../thisInInvalidContextsExternalModule.js | 2 +- .../reference/thisInOuterClassBody.js | 2 +- .../thisInPropertyBoundDeclarations.js | 10 +- .../reference/thisInStaticMethod1.js | 2 +- tests/baselines/reference/thisTypeErrors.js | 2 +- ...peArgumentInferenceWithClassExpression1.js | 14 +- ...peArgumentInferenceWithClassExpression2.js | 14 +- tests/baselines/reference/typeOfPrototype.js | 2 +- .../reference/typeOfThisInStaticMembers2.js | 4 +- tests/baselines/reference/typeQueryOnClass.js | 4 +- .../unqualifiedCallToClassStatic1.js | 8 +- tests/baselines/reference/witness.js | 2 +- tests/cases/unittests/transpile.ts | 128 ++-- 171 files changed, 961 insertions(+), 1031 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index d1821b5b5e074..7b9c4806e0a9d 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -223,7 +223,6 @@ function concatenateFiles(destinationFile, sourceFiles) { var useDebugMode = true; var useTransforms = process.env.USE_TRANSFORMS || false; -var useTransformCompat = false; var host = (process.env.host || process.env.TYPESCRIPT_HOST || "node"); var compilerFilename = "tsc.js"; var LKGCompiler = path.join(LKGDirectory, compilerFilename); @@ -286,9 +285,6 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, noOu if (useBuiltCompiler && useTransforms) { options += " --experimentalTransforms" } - else if (useBuiltCompiler && useTransformCompat) { - options += " --transformCompatibleEmit" - } var cmd = host + " " + compilerPath + " " + options + " "; cmd = cmd + sources.join(" "); @@ -417,10 +413,6 @@ task("setTransforms", function() { useTransforms = true; }); -task("setTransformCompat", function() { - useTransformCompat = true; -}); - task("configure-nightly", [configureNightlyJs], function() { var cmd = host + " " + configureNightlyJs + " " + packageJson + " " + programTs; console.log(cmd); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 307bb5d540339..cc2806f93c140 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -327,13 +327,6 @@ namespace ts { name: "experimentalTransforms", type: "boolean", experimental: true - }, - { - // this option will be removed when this is merged with master and exists solely - // to enable the tree transforming emitter side-by-side with the existing emitter. - name: "transformCompatibleEmit", - type: "boolean", - experimental: true } ]; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 357fec7f0fd65..a3c27d8d817a1 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1915,9 +1915,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge if (multiLine) { decreaseIndent(); - if (!compilerOptions.transformCompatibleEmit) { - writeLine(); - } } write(")"); @@ -2237,7 +2234,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return forEach(elements, e => e.kind === SyntaxKind.SpreadElementExpression); } - function skipParentheses(node: Expression): Expression { + function skipParenthesesAndAssertions(node: Expression): Expression { while (node.kind === SyntaxKind.ParenthesizedExpression || node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression) { node = (node).expression; } @@ -2268,7 +2265,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function emitCallWithSpread(node: CallExpression) { let target: Expression; - const expr = skipParentheses(node.expression); + const expr = skipParenthesesAndAssertions(node.expression); if (expr.kind === SyntaxKind.PropertyAccessExpression) { // Target will be emitted as "this" argument target = emitCallTarget((expr).expression); @@ -4334,9 +4331,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge writeLine(); emitStart(restParam); emitNodeWithCommentsAndWithoutSourcemap(restParam.name); - write(restIndex > 0 || !compilerOptions.transformCompatibleEmit - ? `[${tempName} - ${restIndex}] = arguments[${tempName}];` - : `[${tempName}] = arguments[${tempName}];`); + write(`[${tempName} - ${restIndex}] = arguments[${tempName}];`); emitEnd(restParam); decreaseIndent(); writeLine(); @@ -5356,7 +5351,7 @@ const _super = (function (geti, seti) { const isClassExpressionWithStaticProperties = staticProperties.length > 0 && node.kind === SyntaxKind.ClassExpression; let tempVariable: Identifier; - if (isClassExpressionWithStaticProperties && compilerOptions.transformCompatibleEmit) { + if (isClassExpressionWithStaticProperties) { tempVariable = createAndRecordTempVariable(TempFlags.Auto); write("("); increaseIndent(); @@ -5393,11 +5388,6 @@ const _super = (function (geti, seti) { writeLine(); emitConstructor(node, baseTypeNode); emitMemberFunctionsForES5AndLower(node); - if (!compilerOptions.transformCompatibleEmit) { - emitPropertyDeclarations(node, staticProperties); - writeLine(); - emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); - } writeLine(); emitToken(SyntaxKind.CloseBraceToken, node.members.end, () => { write("return "); @@ -5424,13 +5414,10 @@ const _super = (function (geti, seti) { write("))"); if (node.kind === SyntaxKind.ClassDeclaration) { write(";"); - if (compilerOptions.transformCompatibleEmit) { - emitPropertyDeclarations(node, staticProperties); - writeLine(); - emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); - } + emitPropertyDeclarations(node, staticProperties); + emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); } - else if (isClassExpressionWithStaticProperties && compilerOptions.transformCompatibleEmit) { + else if (isClassExpressionWithStaticProperties) { for (const property of staticProperties) { write(","); writeLine(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fb5ab0c36091e..07d1daec6699c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2473,7 +2473,6 @@ namespace ts { allowJs?: boolean; /* @internal */ stripInternal?: boolean; /* @internal */ experimentalTransforms?: boolean; - /* @internal */ transformCompatibleEmit?: boolean; // Skip checking lib.d.ts to help speed up tests. /* @internal */ skipDefaultLibCheck?: boolean; diff --git a/tests/baselines/reference/ClassAndModuleThatMergeWithStaticVariableAndExportedVarThatShareAName.js b/tests/baselines/reference/ClassAndModuleThatMergeWithStaticVariableAndExportedVarThatShareAName.js index a0cb50e73826c..63d3739846b04 100644 --- a/tests/baselines/reference/ClassAndModuleThatMergeWithStaticVariableAndExportedVarThatShareAName.js +++ b/tests/baselines/reference/ClassAndModuleThatMergeWithStaticVariableAndExportedVarThatShareAName.js @@ -28,9 +28,9 @@ var Point = (function () { this.x = x; this.y = y; } - Point.Origin = { x: 0, y: 0 }; return Point; }()); +Point.Origin = { x: 0, y: 0 }; var Point; (function (Point) { Point.Origin = ""; //expected duplicate identifier error @@ -42,9 +42,9 @@ var A; this.x = x; this.y = y; } - Point.Origin = { x: 0, y: 0 }; return Point; }()); + Point.Origin = { x: 0, y: 0 }; A.Point = Point; var Point; (function (Point) { diff --git a/tests/baselines/reference/ClassAndModuleThatMergeWithStaticVariableAndNonExportedVarThatShareAName.js b/tests/baselines/reference/ClassAndModuleThatMergeWithStaticVariableAndNonExportedVarThatShareAName.js index 3bbf3b98d6ec3..3921546a21cd1 100644 --- a/tests/baselines/reference/ClassAndModuleThatMergeWithStaticVariableAndNonExportedVarThatShareAName.js +++ b/tests/baselines/reference/ClassAndModuleThatMergeWithStaticVariableAndNonExportedVarThatShareAName.js @@ -28,9 +28,9 @@ var Point = (function () { this.x = x; this.y = y; } - Point.Origin = { x: 0, y: 0 }; return Point; }()); +Point.Origin = { x: 0, y: 0 }; var Point; (function (Point) { var Origin = ""; // not an error, since not exported @@ -42,9 +42,9 @@ var A; this.x = x; this.y = y; } - Point.Origin = { x: 0, y: 0 }; return Point; }()); + Point.Origin = { x: 0, y: 0 }; A.Point = Point; var Point; (function (Point) { diff --git a/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.js b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.js index d5c3b3d63f4fc..7633955b22701 100644 --- a/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.js +++ b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.js @@ -7,6 +7,6 @@ class AtomicNumbers { var AtomicNumbers = (function () { function AtomicNumbers() { } - AtomicNumbers.H = 1; return AtomicNumbers; }()); +AtomicNumbers.H = 1; diff --git a/tests/baselines/reference/ES5SymbolProperty1.js b/tests/baselines/reference/ES5SymbolProperty1.js index 1073a33163de7..ab3f420c95cf2 100644 --- a/tests/baselines/reference/ES5SymbolProperty1.js +++ b/tests/baselines/reference/ES5SymbolProperty1.js @@ -14,7 +14,6 @@ obj[Symbol.foo]; var Symbol; var obj = (_a = {}, _a[Symbol.foo] = 0, - _a -); + _a); obj[Symbol.foo]; var _a; diff --git a/tests/baselines/reference/amdImportNotAsPrimaryExpression.js b/tests/baselines/reference/amdImportNotAsPrimaryExpression.js index beb91191bc013..0739792fddd3e 100644 --- a/tests/baselines/reference/amdImportNotAsPrimaryExpression.js +++ b/tests/baselines/reference/amdImportNotAsPrimaryExpression.js @@ -38,9 +38,9 @@ define(["require", "exports"], function (require, exports) { function C1() { this.m1 = 42; } - C1.s1 = true; return C1; }()); + C1.s1 = true; exports.C1 = C1; (function (E1) { E1[E1["A"] = 0] = "A"; diff --git a/tests/baselines/reference/autolift4.js b/tests/baselines/reference/autolift4.js index 8ee1f397d438d..e6790021f63f1 100644 --- a/tests/baselines/reference/autolift4.js +++ b/tests/baselines/reference/autolift4.js @@ -37,9 +37,9 @@ var Point = (function () { Point.prototype.getDist = function () { return Math.sqrt(this.x * this.x + this.y * this.y); }; - Point.origin = new Point(0, 0); return Point; }()); +Point.origin = new Point(0, 0); var Point3D = (function (_super) { __extends(Point3D, _super); function Point3D(x, y, z, m) { diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js index 06eaa9916e31e..0197d919fef87 100644 --- a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js @@ -163,31 +163,35 @@ function foo8() { var x; } function foo9() { - var y = (function () { - function class_3() { - } - class_3.a = x; - return class_3; - }()); + var y = (_a = (function () { + function class_3() { + } + return class_3; + }()), + _a.a = x, + _a); var x; + var _a; } function foo10() { var A = (function () { function A() { } - A.a = x; return A; }()); + A.a = x; var x; } function foo11() { function f() { - var y = (function () { - function class_4() { - } - class_4.a = x; - return class_4; - }()); + var y = (_a = (function () { + function class_4() { + } + return class_4; + }()), + _a.a = x, + _a); + var _a; } var x; } diff --git a/tests/baselines/reference/class2.js b/tests/baselines/reference/class2.js index 6ef78ed46ee9a..91ac89da1092a 100644 --- a/tests/baselines/reference/class2.js +++ b/tests/baselines/reference/class2.js @@ -5,6 +5,6 @@ class foo { constructor() { static f = 3; } } var foo = (function () { function foo() { } - foo.f = 3; return foo; }()); +foo.f = 3; diff --git a/tests/baselines/reference/classExpressionWithDecorator1.js b/tests/baselines/reference/classExpressionWithDecorator1.js index e051e05251bb6..9b52891b5595c 100644 --- a/tests/baselines/reference/classExpressionWithDecorator1.js +++ b/tests/baselines/reference/classExpressionWithDecorator1.js @@ -12,10 +12,10 @@ var v = ; var C = (function () { function C() { } - C.p = 1; - C = __decorate([ - decorate - ], C); return C; }()); +C.p = 1; +C = __decorate([ + decorate +], C); ; diff --git a/tests/baselines/reference/classExpressionWithStaticProperties1.js b/tests/baselines/reference/classExpressionWithStaticProperties1.js index cfe85d97a7184..23d18d7676627 100644 --- a/tests/baselines/reference/classExpressionWithStaticProperties1.js +++ b/tests/baselines/reference/classExpressionWithStaticProperties1.js @@ -2,10 +2,12 @@ var v = class C { static a = 1; static b = 2 }; //// [classExpressionWithStaticProperties1.js] -var v = (function () { - function C() { - } - C.a = 1; - C.b = 2; - return C; -}()); +var v = (_a = (function () { + function C() { + } + return C; + }()), + _a.a = 1, + _a.b = 2, + _a); +var _a; diff --git a/tests/baselines/reference/classExpressionWithStaticProperties2.js b/tests/baselines/reference/classExpressionWithStaticProperties2.js index 134441e0ad950..b9fc03455918f 100644 --- a/tests/baselines/reference/classExpressionWithStaticProperties2.js +++ b/tests/baselines/reference/classExpressionWithStaticProperties2.js @@ -2,9 +2,11 @@ var v = class C { static a = 1; static b }; //// [classExpressionWithStaticProperties2.js] -var v = (function () { - function C() { - } - C.a = 1; - return C; -}()); +var v = (_a = (function () { + function C() { + } + return C; + }()), + _a.a = 1, + _a); +var _a; diff --git a/tests/baselines/reference/classMemberInitializerScoping.js b/tests/baselines/reference/classMemberInitializerScoping.js index 8f78cfb9ac686..52bbaee894570 100644 --- a/tests/baselines/reference/classMemberInitializerScoping.js +++ b/tests/baselines/reference/classMemberInitializerScoping.js @@ -27,9 +27,9 @@ var CCC = (function () { this.y = aaa; this.y = ''; // was: error, cannot assign string to number } - CCC.staticY = aaa; // This shouldnt be error return CCC; }()); +CCC.staticY = aaa; // This shouldnt be error // above is equivalent to this: var aaaa = 1; var CCCC = (function () { diff --git a/tests/baselines/reference/classMemberInitializerWithLamdaScoping.js b/tests/baselines/reference/classMemberInitializerWithLamdaScoping.js index 752220a4cbc5c..c5b661831a23c 100644 --- a/tests/baselines/reference/classMemberInitializerWithLamdaScoping.js +++ b/tests/baselines/reference/classMemberInitializerWithLamdaScoping.js @@ -40,12 +40,12 @@ var Test = (function () { console.log(field); // Using field here shouldnt be error }; } - Test.staticMessageHandler = function () { - var field = Test.field; - console.log(field); // Using field here shouldnt be error - }; return Test; }()); +Test.staticMessageHandler = function () { + var field = Test.field; + console.log(field); // Using field here shouldnt be error +}; var field1; var Test1 = (function () { function Test1(field1) { @@ -56,8 +56,8 @@ var Test1 = (function () { // it would resolve to private field1 and thats not what user intended here. }; } - Test1.staticMessageHandler = function () { - console.log(field1); // This shouldnt be error as its a static property - }; return Test1; }()); +Test1.staticMessageHandler = function () { + console.log(field1); // This shouldnt be error as its a static property +}; diff --git a/tests/baselines/reference/classWithPrivateProperty.js b/tests/baselines/reference/classWithPrivateProperty.js index 6e75797523fb3..c94f1d8ed80a4 100644 --- a/tests/baselines/reference/classWithPrivateProperty.js +++ b/tests/baselines/reference/classWithPrivateProperty.js @@ -32,9 +32,9 @@ var C = (function () { } C.prototype.c = function () { return ''; }; C.f = function () { return ''; }; - C.g = function () { return ''; }; return C; }()); +C.g = function () { return ''; }; var c = new C(); var r1 = c.x; var r2 = c.a; diff --git a/tests/baselines/reference/classWithProtectedProperty.js b/tests/baselines/reference/classWithProtectedProperty.js index 75eadaf4010be..2b2e01e59df10 100644 --- a/tests/baselines/reference/classWithProtectedProperty.js +++ b/tests/baselines/reference/classWithProtectedProperty.js @@ -42,9 +42,9 @@ var C = (function () { } C.prototype.c = function () { return ''; }; C.f = function () { return ''; }; - C.g = function () { return ''; }; return C; }()); +C.g = function () { return ''; }; var D = (function (_super) { __extends(D, _super); function D() { diff --git a/tests/baselines/reference/classWithPublicProperty.js b/tests/baselines/reference/classWithPublicProperty.js index 054713264bd01..47708b7aff391 100644 --- a/tests/baselines/reference/classWithPublicProperty.js +++ b/tests/baselines/reference/classWithPublicProperty.js @@ -30,9 +30,9 @@ var C = (function () { } C.prototype.c = function () { return ''; }; C.f = function () { return ''; }; - C.g = function () { return ''; }; return C; }()); +C.g = function () { return ''; }; // all of these are valid var c = new C(); var r1 = c.x; diff --git a/tests/baselines/reference/cloduleStaticMembers.js b/tests/baselines/reference/cloduleStaticMembers.js index 6095442b6292f..868b9305bdfca 100644 --- a/tests/baselines/reference/cloduleStaticMembers.js +++ b/tests/baselines/reference/cloduleStaticMembers.js @@ -16,10 +16,10 @@ module Clod { var Clod = (function () { function Clod() { } - Clod.x = 10; - Clod.y = 10; return Clod; }()); +Clod.x = 10; +Clod.y = 10; var Clod; (function (Clod) { var p = Clod.x; diff --git a/tests/baselines/reference/commentsOnStaticMembers.js b/tests/baselines/reference/commentsOnStaticMembers.js index 51ed906841d8a..851f9025be350 100644 --- a/tests/baselines/reference/commentsOnStaticMembers.js +++ b/tests/baselines/reference/commentsOnStaticMembers.js @@ -24,13 +24,13 @@ class test { var test = (function () { function test() { } - /** - * p1 comment appears in output - */ - test.p1 = ""; - /** - * p3 comment appears in output - */ - test.p3 = ""; return test; }()); +/** + * p1 comment appears in output + */ +test.p1 = ""; +/** + * p3 comment appears in output + */ +test.p3 = ""; diff --git a/tests/baselines/reference/commonJSImportAsPrimaryExpression.js b/tests/baselines/reference/commonJSImportAsPrimaryExpression.js index 3f49588c10331..b8cf42ea7622a 100644 --- a/tests/baselines/reference/commonJSImportAsPrimaryExpression.js +++ b/tests/baselines/reference/commonJSImportAsPrimaryExpression.js @@ -19,9 +19,9 @@ var C1 = (function () { function C1() { this.m1 = 42; } - C1.s1 = true; return C1; }()); +C1.s1 = true; exports.C1 = C1; //// [foo_1.js] "use strict"; diff --git a/tests/baselines/reference/commonJSImportNotAsPrimaryExpression.js b/tests/baselines/reference/commonJSImportNotAsPrimaryExpression.js index 720554f7e32b1..34d5c343b3c50 100644 --- a/tests/baselines/reference/commonJSImportNotAsPrimaryExpression.js +++ b/tests/baselines/reference/commonJSImportNotAsPrimaryExpression.js @@ -37,9 +37,9 @@ var C1 = (function () { function C1() { this.m1 = 42; } - C1.s1 = true; return C1; }()); +C1.s1 = true; exports.C1 = C1; (function (E1) { E1[E1["A"] = 0] = "A"; diff --git a/tests/baselines/reference/computedPropertyNames10_ES5.js b/tests/baselines/reference/computedPropertyNames10_ES5.js index 14d9235b12b4f..302f9355be850 100644 --- a/tests/baselines/reference/computedPropertyNames10_ES5.js +++ b/tests/baselines/reference/computedPropertyNames10_ES5.js @@ -32,6 +32,5 @@ var v = (_a = {}, _a[true] = function () { }, _a["hello bye"] = function () { }, _a["hello " + a + " bye"] = function () { }, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames11_ES5.js b/tests/baselines/reference/computedPropertyNames11_ES5.js index 0917f82477049..4fc74b30cbec9 100644 --- a/tests/baselines/reference/computedPropertyNames11_ES5.js +++ b/tests/baselines/reference/computedPropertyNames11_ES5.js @@ -68,6 +68,5 @@ var v = (_a = {}, enumerable: true, configurable: true }), - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames12_ES5.js b/tests/baselines/reference/computedPropertyNames12_ES5.js index 2aec8856286cb..10427b5dc13f6 100644 --- a/tests/baselines/reference/computedPropertyNames12_ES5.js +++ b/tests/baselines/reference/computedPropertyNames12_ES5.js @@ -26,6 +26,6 @@ var C = (function () { this[s + n] = 2; this["hello bye"] = 0; } - C["hello " + a + " bye"] = 0; return C; }()); +C["hello " + a + " bye"] = 0; diff --git a/tests/baselines/reference/computedPropertyNames18_ES5.js b/tests/baselines/reference/computedPropertyNames18_ES5.js index b65c7fd4f7dd5..a62af50653196 100644 --- a/tests/baselines/reference/computedPropertyNames18_ES5.js +++ b/tests/baselines/reference/computedPropertyNames18_ES5.js @@ -9,7 +9,6 @@ function foo() { function foo() { var obj = (_a = {}, _a[this.bar] = 0, - _a - ); + _a); var _a; } diff --git a/tests/baselines/reference/computedPropertyNames19_ES5.js b/tests/baselines/reference/computedPropertyNames19_ES5.js index 36f3e66c7c268..bda3e01bbede8 100644 --- a/tests/baselines/reference/computedPropertyNames19_ES5.js +++ b/tests/baselines/reference/computedPropertyNames19_ES5.js @@ -10,7 +10,6 @@ var M; (function (M) { var obj = (_a = {}, _a[this.bar] = 0, - _a - ); + _a); var _a; })(M || (M = {})); diff --git a/tests/baselines/reference/computedPropertyNames1_ES5.js b/tests/baselines/reference/computedPropertyNames1_ES5.js index 4fdec28c7aa41..75bbe909927ce 100644 --- a/tests/baselines/reference/computedPropertyNames1_ES5.js +++ b/tests/baselines/reference/computedPropertyNames1_ES5.js @@ -17,6 +17,5 @@ var v = (_a = {}, enumerable: true, configurable: true }), - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames20_ES5.js b/tests/baselines/reference/computedPropertyNames20_ES5.js index 65acd7fa08f14..1eec0a7bcdba3 100644 --- a/tests/baselines/reference/computedPropertyNames20_ES5.js +++ b/tests/baselines/reference/computedPropertyNames20_ES5.js @@ -6,6 +6,5 @@ var obj = { //// [computedPropertyNames20_ES5.js] var obj = (_a = {}, _a[this.bar] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames22_ES5.js b/tests/baselines/reference/computedPropertyNames22_ES5.js index 5448f4dcf8d1b..721418ee930d4 100644 --- a/tests/baselines/reference/computedPropertyNames22_ES5.js +++ b/tests/baselines/reference/computedPropertyNames22_ES5.js @@ -15,8 +15,7 @@ var C = (function () { C.prototype.bar = function () { var obj = (_a = {}, _a[this.bar()] = function () { }, - _a - ); + _a); return 0; var _a; }; diff --git a/tests/baselines/reference/computedPropertyNames25_ES5.js b/tests/baselines/reference/computedPropertyNames25_ES5.js index f38ac0b2e0d30..b03dd285ced24 100644 --- a/tests/baselines/reference/computedPropertyNames25_ES5.js +++ b/tests/baselines/reference/computedPropertyNames25_ES5.js @@ -35,8 +35,7 @@ var C = (function (_super) { C.prototype.foo = function () { var obj = (_a = {}, _a[_super.prototype.bar.call(this)] = function () { }, - _a - ); + _a); return 0; var _a; }; diff --git a/tests/baselines/reference/computedPropertyNames28_ES5.js b/tests/baselines/reference/computedPropertyNames28_ES5.js index b5b1fdf77aab7..ed15bbf0145cf 100644 --- a/tests/baselines/reference/computedPropertyNames28_ES5.js +++ b/tests/baselines/reference/computedPropertyNames28_ES5.js @@ -27,8 +27,7 @@ var C = (function (_super) { _super.call(this); var obj = (_a = {}, _a[(_super.call(this), "prop")] = function () { }, - _a - ); + _a); var _a; } return C; diff --git a/tests/baselines/reference/computedPropertyNames29_ES5.js b/tests/baselines/reference/computedPropertyNames29_ES5.js index 46ba529d7a546..af6f93c8ad9ba 100644 --- a/tests/baselines/reference/computedPropertyNames29_ES5.js +++ b/tests/baselines/reference/computedPropertyNames29_ES5.js @@ -19,8 +19,7 @@ var C = (function () { (function () { var obj = (_a = {}, _a[_this.bar()] = function () { }, - _a - ); + _a); var _a; }); return 0; diff --git a/tests/baselines/reference/computedPropertyNames30_ES5.js b/tests/baselines/reference/computedPropertyNames30_ES5.js index e787689d3938c..a900fd44c677e 100644 --- a/tests/baselines/reference/computedPropertyNames30_ES5.js +++ b/tests/baselines/reference/computedPropertyNames30_ES5.js @@ -36,8 +36,7 @@ var C = (function (_super) { // illegal, and not capturing this is consistent with //treatment of other similar violations. _a[(_super.call(this), "prop")] = function () { }, - _a - ); + _a); var _a; }); } diff --git a/tests/baselines/reference/computedPropertyNames31_ES5.js b/tests/baselines/reference/computedPropertyNames31_ES5.js index 8417f103fb77b..86fb6655a27c4 100644 --- a/tests/baselines/reference/computedPropertyNames31_ES5.js +++ b/tests/baselines/reference/computedPropertyNames31_ES5.js @@ -39,8 +39,7 @@ var C = (function (_super) { (function () { var obj = (_a = {}, _a[_super.prototype.bar.call(_this)] = function () { }, - _a - ); + _a); var _a; }); return 0; diff --git a/tests/baselines/reference/computedPropertyNames33_ES5.js b/tests/baselines/reference/computedPropertyNames33_ES5.js index 8db62818e22ce..e6d46b03c100e 100644 --- a/tests/baselines/reference/computedPropertyNames33_ES5.js +++ b/tests/baselines/reference/computedPropertyNames33_ES5.js @@ -17,8 +17,7 @@ var C = (function () { C.prototype.bar = function () { var obj = (_a = {}, _a[foo()] = function () { }, - _a - ); + _a); return 0; var _a; }; diff --git a/tests/baselines/reference/computedPropertyNames34_ES5.js b/tests/baselines/reference/computedPropertyNames34_ES5.js index fa0af2897f7fa..3109c400422d0 100644 --- a/tests/baselines/reference/computedPropertyNames34_ES5.js +++ b/tests/baselines/reference/computedPropertyNames34_ES5.js @@ -17,8 +17,7 @@ var C = (function () { C.bar = function () { var obj = (_a = {}, _a[foo()] = function () { }, - _a - ); + _a); return 0; var _a; }; diff --git a/tests/baselines/reference/computedPropertyNames46_ES5.js b/tests/baselines/reference/computedPropertyNames46_ES5.js index 307dadcbe9115..815f5769f3b28 100644 --- a/tests/baselines/reference/computedPropertyNames46_ES5.js +++ b/tests/baselines/reference/computedPropertyNames46_ES5.js @@ -6,6 +6,5 @@ var o = { //// [computedPropertyNames46_ES5.js] var o = (_a = {}, _a["" || 0] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames47_ES5.js b/tests/baselines/reference/computedPropertyNames47_ES5.js index d03b614b29b6b..a2fff2110b49f 100644 --- a/tests/baselines/reference/computedPropertyNames47_ES5.js +++ b/tests/baselines/reference/computedPropertyNames47_ES5.js @@ -16,6 +16,5 @@ var E2; })(E2 || (E2 = {})); var o = (_a = {}, _a[E1.x || E2.x] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames48_ES5.js b/tests/baselines/reference/computedPropertyNames48_ES5.js index 15123a98f302b..55b08ef8301d3 100644 --- a/tests/baselines/reference/computedPropertyNames48_ES5.js +++ b/tests/baselines/reference/computedPropertyNames48_ES5.js @@ -25,14 +25,11 @@ var E; var a; extractIndexer((_a = {}, _a[a] = "", - _a -)); // Should return string + _a)); // Should return string extractIndexer((_b = {}, _b[E.x] = "", - _b -)); // Should return string + _b)); // Should return string extractIndexer((_c = {}, _c["" || 0] = "", - _c -)); // Should return any (widened form of undefined) + _c)); // Should return any (widened form of undefined) var _a, _b, _c; diff --git a/tests/baselines/reference/computedPropertyNames49_ES5.js b/tests/baselines/reference/computedPropertyNames49_ES5.js index 3427ea665a051..12c3430fbe94f 100644 --- a/tests/baselines/reference/computedPropertyNames49_ES5.js +++ b/tests/baselines/reference/computedPropertyNames49_ES5.js @@ -62,6 +62,5 @@ var x = (_a = { }), , _a.p2 = 20, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames4_ES5.js b/tests/baselines/reference/computedPropertyNames4_ES5.js index f91476c79c160..ac0fa8ec93792 100644 --- a/tests/baselines/reference/computedPropertyNames4_ES5.js +++ b/tests/baselines/reference/computedPropertyNames4_ES5.js @@ -32,6 +32,5 @@ var v = (_a = {}, _a[true] = 0, _a["hello bye"] = 0, _a["hello " + a + " bye"] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames50_ES5.js b/tests/baselines/reference/computedPropertyNames50_ES5.js index a74561295bec3..db9a03578dfa0 100644 --- a/tests/baselines/reference/computedPropertyNames50_ES5.js +++ b/tests/baselines/reference/computedPropertyNames50_ES5.js @@ -58,6 +58,5 @@ var x = (_a = { }), , _a.p2 = 20, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames5_ES5.js b/tests/baselines/reference/computedPropertyNames5_ES5.js index 58c5af2efa3b4..3367faa2c20b2 100644 --- a/tests/baselines/reference/computedPropertyNames5_ES5.js +++ b/tests/baselines/reference/computedPropertyNames5_ES5.js @@ -18,6 +18,5 @@ var v = (_a = {}, _a[{}] = 0, _a[undefined] = undefined, _a[null] = null, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames6_ES5.js b/tests/baselines/reference/computedPropertyNames6_ES5.js index 7e9c62029403d..29d035bfcbfb4 100644 --- a/tests/baselines/reference/computedPropertyNames6_ES5.js +++ b/tests/baselines/reference/computedPropertyNames6_ES5.js @@ -16,6 +16,5 @@ var v = (_a = {}, _a[p1] = 0, _a[p2] = 1, _a[p3] = 2, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames7_ES5.js b/tests/baselines/reference/computedPropertyNames7_ES5.js index 3311ec7e092db..01cf2efc030b8 100644 --- a/tests/baselines/reference/computedPropertyNames7_ES5.js +++ b/tests/baselines/reference/computedPropertyNames7_ES5.js @@ -13,6 +13,5 @@ var E; })(E || (E = {})); var v = (_a = {}, _a[E.member] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNames8_ES5.js b/tests/baselines/reference/computedPropertyNames8_ES5.js index 6eceee81adb10..82d262c7e4e11 100644 --- a/tests/baselines/reference/computedPropertyNames8_ES5.js +++ b/tests/baselines/reference/computedPropertyNames8_ES5.js @@ -15,7 +15,6 @@ function f() { var v = (_a = {}, _a[t] = 0, _a[u] = 1, - _a - ); + _a); var _a; } diff --git a/tests/baselines/reference/computedPropertyNames9_ES5.js b/tests/baselines/reference/computedPropertyNames9_ES5.js index 6c1d7bcb4d076..b1ac6c9e3b656 100644 --- a/tests/baselines/reference/computedPropertyNames9_ES5.js +++ b/tests/baselines/reference/computedPropertyNames9_ES5.js @@ -16,6 +16,5 @@ var v = (_a = {}, _a[f("")] = 0, _a[f(0)] = 0, _a[f(true)] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js b/tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js index 1f7a3ae123e57..d8a33951d4a26 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js @@ -12,6 +12,5 @@ var o: I = { var o = (_a = {}, _a[+"foo"] = "", _a[+"bar"] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js b/tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js index c4bfde03531ef..1ee8835115415 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js @@ -13,6 +13,5 @@ var o: I = { var o = (_a = {}, _a["" + 0] = function (y) { return y.length; }, _a["" + 1] = function (y) { return y.length; }, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js b/tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js index 945475f8533f2..6be09e4bef3f3 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js @@ -13,6 +13,5 @@ var o: I = { var o = (_a = {}, _a[+"foo"] = function (y) { return y.length; }, _a[+"bar"] = function (y) { return y.length; }, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js b/tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js index 98042f201e339..b510cda2916f4 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js @@ -12,6 +12,5 @@ var o: I = { var o = (_a = {}, _a[+"foo"] = function (y) { return y.length; }, _a[+"bar"] = function (y) { return y.length; }, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js b/tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js index b17f91be38b41..0c319a526f4a1 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js @@ -13,6 +13,5 @@ var o: I = { var o = (_a = {}, _a["" + "foo"] = "", _a["" + "bar"] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js b/tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js index c7a728e5ef85f..51d78be59d4ff 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js @@ -13,6 +13,5 @@ var o: I = { var o = (_a = {}, _a[+"foo"] = "", _a[+"bar"] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js b/tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js index c97b7c5b1f865..c6ef45d7fabe0 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js @@ -21,6 +21,5 @@ foo((_a = { _a["hi" + "bye"] = true, _a[0 + 1] = 0, _a[+"hi"] = [0], - _a -)); + _a)); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js b/tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js index 9ca7e826aa713..9caf5f2aebfde 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js @@ -21,6 +21,5 @@ foo((_a = { _a["hi" + "bye"] = true, _a[0 + 1] = 0, _a[+"hi"] = [0], - _a -)); + _a)); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js b/tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js index 419ea906550b5..24f6218864cbd 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js @@ -13,6 +13,5 @@ var o: I = { var o = (_a = {}, _a["" + "foo"] = "", _a["" + "bar"] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js b/tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js index d3beb8b8deb73..340802da2af91 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js @@ -13,6 +13,5 @@ var o: I = { var o = (_a = {}, _a[+"foo"] = "", _a[+"bar"] = 0, - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js b/tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js index e77eb79dc709e..c903b8648292a 100644 --- a/tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js @@ -20,8 +20,7 @@ var v = (_a = {}, enumerable: true, configurable: true }), - _a -); + _a); var _a; diff --git a/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js b/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js index 39e393c46cce6..972aaf9b52f6a 100644 --- a/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js +++ b/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js @@ -10,7 +10,6 @@ var v = (_a = {}, _a["hello"] = function () { debugger; }, - _a -); + _a); var _a; //# sourceMappingURL=computedPropertyNamesSourceMap2_ES5.js.map \ No newline at end of file diff --git a/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map b/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map index baf1e998efdc5..bdc2149765166 100644 --- a/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map +++ b/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map @@ -1,2 +1,2 @@ //// [computedPropertyNamesSourceMap2_ES5.js.map] -{"version":3,"file":"computedPropertyNamesSourceMap2_ES5.js","sourceRoot":"","sources":["computedPropertyNamesSourceMap2_ES5.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG;IACJ,GAAC,OAAO,CAAC,GAAT;QACI,QAAQ,CAAC;IACb,CAAC;;CACJ,CAAA"} \ No newline at end of file +{"version":3,"file":"computedPropertyNamesSourceMap2_ES5.js","sourceRoot":"","sources":["computedPropertyNamesSourceMap2_ES5.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG;IACJ,GAAC,OAAO,CAAC,GAAT;QACI,QAAQ,CAAC;IACb,CAAC;OACJ,CAAA"} \ No newline at end of file diff --git a/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt b/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt index f6501777985f3..0535196f9235c 100644 --- a/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt +++ b/tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt @@ -56,23 +56,21 @@ sourceFile:computedPropertyNamesSourceMap2_ES5.ts >>> }, 1 >^^^^ 2 > ^ -3 > ^^-> +3 > ^^^^-> 1 > > 2 > } 1 >Emitted(4, 5) Source(4, 5) + SourceIndex(0) 2 >Emitted(4, 6) Source(4, 6) + SourceIndex(0) --- ->>> _a ->>>); -1->^ -2 > ^ -3 > ^^^^^^-> +>>> _a); +1->^^^^^^^ +2 > ^ 1-> >} -2 > -1->Emitted(6, 2) Source(5, 2) + SourceIndex(0) -2 >Emitted(6, 3) Source(5, 2) + SourceIndex(0) +2 > +1->Emitted(5, 8) Source(5, 2) + SourceIndex(0) +2 >Emitted(5, 9) Source(5, 2) + SourceIndex(0) --- >>>var _a; >>>//# sourceMappingURL=computedPropertyNamesSourceMap2_ES5.js.map \ No newline at end of file diff --git a/tests/baselines/reference/contextuallyTypedClassExpressionMethodDeclaration01.js b/tests/baselines/reference/contextuallyTypedClassExpressionMethodDeclaration01.js index 11081be4fb356..2c8efdd424aab 100644 --- a/tests/baselines/reference/contextuallyTypedClassExpressionMethodDeclaration01.js +++ b/tests/baselines/reference/contextuallyTypedClassExpressionMethodDeclaration01.js @@ -61,28 +61,32 @@ function getFoo1() { }()); } function getFoo2() { - return (function () { - function class_2() { - } - class_2.method1 = function (arg) { + return (_a = (function () { + function class_2() { + } + return class_2; + }()), + _a.method1 = function (arg) { arg.numProp = 10; - }; - class_2.method2 = function (arg) { + }, + _a.method2 = function (arg) { arg.strProp = "hello"; - }; - return class_2; - }()); + }, + _a); + var _a; } function getFoo3() { - return (function () { - function class_3() { - } - class_3.method1 = function (arg) { + return (_a = (function () { + function class_3() { + } + return class_3; + }()), + _a.method1 = function (arg) { arg.numProp = 10; - }; - class_3.method2 = function (arg) { + }, + _a.method2 = function (arg) { arg.strProp = "hello"; - }; - return class_3; - }()); + }, + _a); + var _a; } diff --git a/tests/baselines/reference/declFilePrivateStatic.js b/tests/baselines/reference/declFilePrivateStatic.js index 30cd85444cf7b..fb24bea766088 100644 --- a/tests/baselines/reference/declFilePrivateStatic.js +++ b/tests/baselines/reference/declFilePrivateStatic.js @@ -40,10 +40,10 @@ var C = (function () { enumerable: true, configurable: true }); - C.x = 1; - C.y = 1; return C; }()); +C.x = 1; +C.y = 1; //// [declFilePrivateStatic.d.ts] diff --git a/tests/baselines/reference/decoratorCallGeneric.js b/tests/baselines/reference/decoratorCallGeneric.js index acc9c48297dfe..a1d470b7063c3 100644 --- a/tests/baselines/reference/decoratorCallGeneric.js +++ b/tests/baselines/reference/decoratorCallGeneric.js @@ -24,8 +24,8 @@ var C = (function () { function C() { } C.m = function () { }; - C = __decorate([ - dec - ], C); return C; }()); +C = __decorate([ + dec +], C); diff --git a/tests/baselines/reference/decoratorChecksFunctionBodies.js b/tests/baselines/reference/decoratorChecksFunctionBodies.js index d70c4a334608e..68dbe6bd1ef0c 100644 --- a/tests/baselines/reference/decoratorChecksFunctionBodies.js +++ b/tests/baselines/reference/decoratorChecksFunctionBodies.js @@ -30,12 +30,12 @@ var A = (function () { } A.prototype.m = function () { }; - __decorate([ - (function (x, p) { - var a = 3; - func(a); - return x; - }) - ], A.prototype, "m", null); return A; }()); +__decorate([ + (function (x, p) { + var a = 3; + func(a); + return x; + }) +], A.prototype, "m", null); diff --git a/tests/baselines/reference/decoratorInstantiateModulesInFunctionBodies.js b/tests/baselines/reference/decoratorInstantiateModulesInFunctionBodies.js index 92dcc1f836c5c..28b620a3e8c4c 100644 --- a/tests/baselines/reference/decoratorInstantiateModulesInFunctionBodies.js +++ b/tests/baselines/reference/decoratorInstantiateModulesInFunctionBodies.js @@ -45,8 +45,8 @@ var Wat = (function () { Wat.whatever = function () { // ... }; - __decorate([ - filter(function () { return a_1.test == 'abc'; }) - ], Wat, "whatever", null); return Wat; }()); +__decorate([ + filter(function () { return a_1.test == 'abc'; }) +], Wat, "whatever", null); diff --git a/tests/baselines/reference/decoratorMetadata.js b/tests/baselines/reference/decoratorMetadata.js index 2f360df061856..d95658c7f18df 100644 --- a/tests/baselines/reference/decoratorMetadata.js +++ b/tests/baselines/reference/decoratorMetadata.js @@ -45,15 +45,15 @@ var MyComponent = (function () { } MyComponent.prototype.method = function (x) { }; - __decorate([ - decorator, - __metadata('design:type', Function), - __metadata('design:paramtypes', [Object]), - __metadata('design:returntype', void 0) - ], MyComponent.prototype, "method", null); - MyComponent = __decorate([ - decorator, - __metadata('design:paramtypes', [service_1.default]) - ], MyComponent); return MyComponent; }()); +__decorate([ + decorator, + __metadata('design:type', Function), + __metadata('design:paramtypes', [Object]), + __metadata('design:returntype', void 0) +], MyComponent.prototype, "method", null); +MyComponent = __decorate([ + decorator, + __metadata('design:paramtypes', [service_1.default]) +], MyComponent); diff --git a/tests/baselines/reference/decoratorMetadataForMethodWithNoReturnTypeAnnotation01.js b/tests/baselines/reference/decoratorMetadataForMethodWithNoReturnTypeAnnotation01.js index f5048ead6313d..e5127987d532b 100644 --- a/tests/baselines/reference/decoratorMetadataForMethodWithNoReturnTypeAnnotation01.js +++ b/tests/baselines/reference/decoratorMetadataForMethodWithNoReturnTypeAnnotation01.js @@ -20,11 +20,11 @@ var MyClass = (function () { } MyClass.prototype.doSomething = function () { }; - __decorate([ - decorator, - __metadata('design:type', Function), - __metadata('design:paramtypes', []), - __metadata('design:returntype', void 0) - ], MyClass.prototype, "doSomething", null); return MyClass; }()); +__decorate([ + decorator, + __metadata('design:type', Function), + __metadata('design:paramtypes', []), + __metadata('design:returntype', void 0) +], MyClass.prototype, "doSomething", null); diff --git a/tests/baselines/reference/decoratorMetadataOnInferredType.js b/tests/baselines/reference/decoratorMetadataOnInferredType.js index 5a39a6ce42ca5..47b6c45e80636 100644 --- a/tests/baselines/reference/decoratorMetadataOnInferredType.js +++ b/tests/baselines/reference/decoratorMetadataOnInferredType.js @@ -31,10 +31,10 @@ var B = (function () { function B() { this.x = new A(); } - __decorate([ - decorator, - __metadata('design:type', Object) - ], B.prototype, "x", void 0); return B; }()); +__decorate([ + decorator, + __metadata('design:type', Object) +], B.prototype, "x", void 0); exports.B = B; diff --git a/tests/baselines/reference/decoratorMetadataWithConstructorType.js b/tests/baselines/reference/decoratorMetadataWithConstructorType.js index 39e8d1811aa7a..0559e62754c57 100644 --- a/tests/baselines/reference/decoratorMetadataWithConstructorType.js +++ b/tests/baselines/reference/decoratorMetadataWithConstructorType.js @@ -31,10 +31,10 @@ var B = (function () { function B() { this.x = new A(); } - __decorate([ - decorator, - __metadata('design:type', A) - ], B.prototype, "x", void 0); return B; }()); +__decorate([ + decorator, + __metadata('design:type', A) +], B.prototype, "x", void 0); exports.B = B; diff --git a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision.js b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision.js index b549d28c7f182..a0bc68bd8d884 100644 --- a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision.js +++ b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision.js @@ -44,10 +44,10 @@ var MyClass = (function () { this.db = db; this.db.doSomething(); } - MyClass = __decorate([ - someDecorator, - __metadata('design:paramtypes', [db_1.db]) - ], MyClass); return MyClass; }()); +MyClass = __decorate([ + someDecorator, + __metadata('design:paramtypes', [db_1.db]) +], MyClass); exports.MyClass = MyClass; diff --git a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision2.js b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision2.js index acd86e78d76e6..49a2983ce708e 100644 --- a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision2.js +++ b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision2.js @@ -44,10 +44,10 @@ var MyClass = (function () { this.db = db; this.db.doSomething(); } - MyClass = __decorate([ - someDecorator, - __metadata('design:paramtypes', [db_1.db]) - ], MyClass); return MyClass; }()); +MyClass = __decorate([ + someDecorator, + __metadata('design:paramtypes', [db_1.db]) +], MyClass); exports.MyClass = MyClass; diff --git a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision3.js b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision3.js index 2acd829c107b6..f3c1d1b5da551 100644 --- a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision3.js +++ b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision3.js @@ -44,10 +44,10 @@ var MyClass = (function () { this.db = db; this.db.doSomething(); } - MyClass = __decorate([ - someDecorator, - __metadata('design:paramtypes', [db.db]) - ], MyClass); return MyClass; }()); +MyClass = __decorate([ + someDecorator, + __metadata('design:paramtypes', [db.db]) +], MyClass); exports.MyClass = MyClass; diff --git a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision4.js b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision4.js index 4af0beeddcb99..3448352eaaf0b 100644 --- a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision4.js +++ b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision4.js @@ -44,10 +44,10 @@ var MyClass = (function () { this.db = db; this.db.doSomething(); } - MyClass = __decorate([ - someDecorator, - __metadata('design:paramtypes', [Object]) - ], MyClass); return MyClass; }()); +MyClass = __decorate([ + someDecorator, + __metadata('design:paramtypes', [Object]) +], MyClass); exports.MyClass = MyClass; diff --git a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision5.js b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision5.js index f1537e3b447bc..8eae92ec4d160 100644 --- a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision5.js +++ b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision5.js @@ -45,10 +45,10 @@ var MyClass = (function () { this.db = db; this.db.doSomething(); } - MyClass = __decorate([ - someDecorator, - __metadata('design:paramtypes', [db_1.default]) - ], MyClass); return MyClass; }()); +MyClass = __decorate([ + someDecorator, + __metadata('design:paramtypes', [db_1.default]) +], MyClass); exports.MyClass = MyClass; diff --git a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision6.js b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision6.js index 7ef2c28881067..26e714c16a134 100644 --- a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision6.js +++ b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision6.js @@ -45,10 +45,10 @@ var MyClass = (function () { this.db = db; this.db.doSomething(); } - MyClass = __decorate([ - someDecorator, - __metadata('design:paramtypes', [db_1.default]) - ], MyClass); return MyClass; }()); +MyClass = __decorate([ + someDecorator, + __metadata('design:paramtypes', [db_1.default]) +], MyClass); exports.MyClass = MyClass; diff --git a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision7.js b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision7.js index f8469cdd6a432..1d3b8a92349bf 100644 --- a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision7.js +++ b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision7.js @@ -45,10 +45,10 @@ var MyClass = (function () { this.db = db; this.db.doSomething(); } - MyClass = __decorate([ - someDecorator, - __metadata('design:paramtypes', [Object]) - ], MyClass); return MyClass; }()); +MyClass = __decorate([ + someDecorator, + __metadata('design:paramtypes', [Object]) +], MyClass); exports.MyClass = MyClass; diff --git a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision8.js b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision8.js index f1885ccfaec29..910008b9c271b 100644 --- a/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision8.js +++ b/tests/baselines/reference/decoratorMetadataWithImportDeclarationNameCollision8.js @@ -44,10 +44,10 @@ var MyClass = (function () { this.db = db; this.db.doSomething(); } - MyClass = __decorate([ - someDecorator, - __metadata('design:paramtypes', [database.db]) - ], MyClass); return MyClass; }()); +MyClass = __decorate([ + someDecorator, + __metadata('design:paramtypes', [database.db]) +], MyClass); exports.MyClass = MyClass; diff --git a/tests/baselines/reference/decoratorOnClass1.js b/tests/baselines/reference/decoratorOnClass1.js index 7ea46b4698fc8..5cff4c2649c27 100644 --- a/tests/baselines/reference/decoratorOnClass1.js +++ b/tests/baselines/reference/decoratorOnClass1.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - C = __decorate([ - dec - ], C); return C; }()); +C = __decorate([ + dec +], C); diff --git a/tests/baselines/reference/decoratorOnClass2.js b/tests/baselines/reference/decoratorOnClass2.js index 3f24c7ae55b7b..4d9c2007ad0f2 100644 --- a/tests/baselines/reference/decoratorOnClass2.js +++ b/tests/baselines/reference/decoratorOnClass2.js @@ -16,9 +16,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - C = __decorate([ - dec - ], C); return C; }()); +C = __decorate([ + dec +], C); exports.C = C; diff --git a/tests/baselines/reference/decoratorOnClass3.js b/tests/baselines/reference/decoratorOnClass3.js index 5a3601fc1f3e8..fd09a6d8de576 100644 --- a/tests/baselines/reference/decoratorOnClass3.js +++ b/tests/baselines/reference/decoratorOnClass3.js @@ -16,8 +16,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - C = __decorate([ - dec - ], C); return C; }()); +C = __decorate([ + dec +], C); diff --git a/tests/baselines/reference/decoratorOnClass4.js b/tests/baselines/reference/decoratorOnClass4.js index adbe30db662a6..daf19e20327b5 100644 --- a/tests/baselines/reference/decoratorOnClass4.js +++ b/tests/baselines/reference/decoratorOnClass4.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - C = __decorate([ - dec() - ], C); return C; }()); +C = __decorate([ + dec() +], C); diff --git a/tests/baselines/reference/decoratorOnClass5.js b/tests/baselines/reference/decoratorOnClass5.js index 6741b26373bb9..70fc551514070 100644 --- a/tests/baselines/reference/decoratorOnClass5.js +++ b/tests/baselines/reference/decoratorOnClass5.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - C = __decorate([ - dec() - ], C); return C; }()); +C = __decorate([ + dec() +], C); diff --git a/tests/baselines/reference/decoratorOnClass8.js b/tests/baselines/reference/decoratorOnClass8.js index e5cd8812ea780..ae50ab75ab8e7 100644 --- a/tests/baselines/reference/decoratorOnClass8.js +++ b/tests/baselines/reference/decoratorOnClass8.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - C = __decorate([ - dec() - ], C); return C; }()); +C = __decorate([ + dec() +], C); diff --git a/tests/baselines/reference/decoratorOnClassAccessor1.js b/tests/baselines/reference/decoratorOnClassAccessor1.js index cf989705b79b4..27966350faecd 100644 --- a/tests/baselines/reference/decoratorOnClassAccessor1.js +++ b/tests/baselines/reference/decoratorOnClassAccessor1.js @@ -20,8 +20,8 @@ var C = (function () { enumerable: true, configurable: true }); - __decorate([ - dec - ], C.prototype, "accessor", null); return C; }()); +__decorate([ + dec +], C.prototype, "accessor", null); diff --git a/tests/baselines/reference/decoratorOnClassAccessor2.js b/tests/baselines/reference/decoratorOnClassAccessor2.js index 9e9455b438051..57cb3b50461b3 100644 --- a/tests/baselines/reference/decoratorOnClassAccessor2.js +++ b/tests/baselines/reference/decoratorOnClassAccessor2.js @@ -20,8 +20,8 @@ var C = (function () { enumerable: true, configurable: true }); - __decorate([ - dec - ], C.prototype, "accessor", null); return C; }()); +__decorate([ + dec +], C.prototype, "accessor", null); diff --git a/tests/baselines/reference/decoratorOnClassAccessor3.js b/tests/baselines/reference/decoratorOnClassAccessor3.js index 8a27914e0c898..c02f984e70ac3 100644 --- a/tests/baselines/reference/decoratorOnClassAccessor3.js +++ b/tests/baselines/reference/decoratorOnClassAccessor3.js @@ -20,8 +20,8 @@ var C = (function () { enumerable: true, configurable: true }); - __decorate([ - dec - ], C.prototype, "accessor", null); return C; }()); +__decorate([ + dec +], C.prototype, "accessor", null); diff --git a/tests/baselines/reference/decoratorOnClassAccessor4.js b/tests/baselines/reference/decoratorOnClassAccessor4.js index 85b35e95cef54..0e0af58f526be 100644 --- a/tests/baselines/reference/decoratorOnClassAccessor4.js +++ b/tests/baselines/reference/decoratorOnClassAccessor4.js @@ -20,8 +20,8 @@ var C = (function () { enumerable: true, configurable: true }); - __decorate([ - dec - ], C.prototype, "accessor", null); return C; }()); +__decorate([ + dec +], C.prototype, "accessor", null); diff --git a/tests/baselines/reference/decoratorOnClassAccessor5.js b/tests/baselines/reference/decoratorOnClassAccessor5.js index 12ec7a08adcb9..c7d5d109cc07f 100644 --- a/tests/baselines/reference/decoratorOnClassAccessor5.js +++ b/tests/baselines/reference/decoratorOnClassAccessor5.js @@ -20,8 +20,8 @@ var C = (function () { enumerable: true, configurable: true }); - __decorate([ - dec - ], C.prototype, "accessor", null); return C; }()); +__decorate([ + dec +], C.prototype, "accessor", null); diff --git a/tests/baselines/reference/decoratorOnClassAccessor6.js b/tests/baselines/reference/decoratorOnClassAccessor6.js index d5477deb5af10..8ac40e722995c 100644 --- a/tests/baselines/reference/decoratorOnClassAccessor6.js +++ b/tests/baselines/reference/decoratorOnClassAccessor6.js @@ -20,8 +20,8 @@ var C = (function () { enumerable: true, configurable: true }); - __decorate([ - dec - ], C.prototype, "accessor", null); return C; }()); +__decorate([ + dec +], C.prototype, "accessor", null); diff --git a/tests/baselines/reference/decoratorOnClassConstructorParameter1.js b/tests/baselines/reference/decoratorOnClassConstructorParameter1.js index fe04f569e30a7..176e2309ae05f 100644 --- a/tests/baselines/reference/decoratorOnClassConstructorParameter1.js +++ b/tests/baselines/reference/decoratorOnClassConstructorParameter1.js @@ -18,8 +18,8 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { var C = (function () { function C(p) { } - C = __decorate([ - __param(0, dec) - ], C); return C; }()); +C = __decorate([ + __param(0, dec) +], C); diff --git a/tests/baselines/reference/decoratorOnClassConstructorParameter4.js b/tests/baselines/reference/decoratorOnClassConstructorParameter4.js index 8d02bd467d6dc..e14f035880878 100644 --- a/tests/baselines/reference/decoratorOnClassConstructorParameter4.js +++ b/tests/baselines/reference/decoratorOnClassConstructorParameter4.js @@ -18,8 +18,8 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { var C = (function () { function C(public, p) { } - C = __decorate([ - __param(1, dec) - ], C); return C; }()); +C = __decorate([ + __param(1, dec) +], C); diff --git a/tests/baselines/reference/decoratorOnClassMethod1.js b/tests/baselines/reference/decoratorOnClassMethod1.js index ef029b845431b..b93d7f88771ed 100644 --- a/tests/baselines/reference/decoratorOnClassMethod1.js +++ b/tests/baselines/reference/decoratorOnClassMethod1.js @@ -16,8 +16,8 @@ var C = (function () { function C() { } C.prototype.method = function () { }; - __decorate([ - dec - ], C.prototype, "method", null); return C; }()); +__decorate([ + dec +], C.prototype, "method", null); diff --git a/tests/baselines/reference/decoratorOnClassMethod10.js b/tests/baselines/reference/decoratorOnClassMethod10.js index f90251d619694..bfa57f0aeb704 100644 --- a/tests/baselines/reference/decoratorOnClassMethod10.js +++ b/tests/baselines/reference/decoratorOnClassMethod10.js @@ -16,8 +16,8 @@ var C = (function () { function C() { } C.prototype.method = function () { }; - __decorate([ - dec - ], C.prototype, "method", null); return C; }()); +__decorate([ + dec +], C.prototype, "method", null); diff --git a/tests/baselines/reference/decoratorOnClassMethod11.js b/tests/baselines/reference/decoratorOnClassMethod11.js index f1589a39286fa..c703459b88d65 100644 --- a/tests/baselines/reference/decoratorOnClassMethod11.js +++ b/tests/baselines/reference/decoratorOnClassMethod11.js @@ -22,9 +22,9 @@ var M; } C.prototype.decorator = function (target, key) { }; C.prototype.method = function () { }; - __decorate([ - this.decorator - ], C.prototype, "method", null); return C; }()); + __decorate([ + this.decorator + ], C.prototype, "method", null); })(M || (M = {})); diff --git a/tests/baselines/reference/decoratorOnClassMethod12.js b/tests/baselines/reference/decoratorOnClassMethod12.js index 0650f1d00ee25..6e9b9763a3ab9 100644 --- a/tests/baselines/reference/decoratorOnClassMethod12.js +++ b/tests/baselines/reference/decoratorOnClassMethod12.js @@ -35,9 +35,9 @@ var M; _super.apply(this, arguments); } C.prototype.method = function () { }; - __decorate([ - _super.decorator - ], C.prototype, "method", null); return C; }(S)); + __decorate([ + _super.decorator + ], C.prototype, "method", null); })(M || (M = {})); diff --git a/tests/baselines/reference/decoratorOnClassMethod2.js b/tests/baselines/reference/decoratorOnClassMethod2.js index 5db2922ed7187..98ad8ad43ad78 100644 --- a/tests/baselines/reference/decoratorOnClassMethod2.js +++ b/tests/baselines/reference/decoratorOnClassMethod2.js @@ -16,8 +16,8 @@ var C = (function () { function C() { } C.prototype.method = function () { }; - __decorate([ - dec - ], C.prototype, "method", null); return C; }()); +__decorate([ + dec +], C.prototype, "method", null); diff --git a/tests/baselines/reference/decoratorOnClassMethod3.js b/tests/baselines/reference/decoratorOnClassMethod3.js index 30f527368f28b..4a5943681d4cf 100644 --- a/tests/baselines/reference/decoratorOnClassMethod3.js +++ b/tests/baselines/reference/decoratorOnClassMethod3.js @@ -16,8 +16,8 @@ var C = (function () { function C() { } C.prototype.method = function () { }; - __decorate([ - dec - ], C.prototype, "method", null); return C; }()); +__decorate([ + dec +], C.prototype, "method", null); diff --git a/tests/baselines/reference/decoratorOnClassMethod8.js b/tests/baselines/reference/decoratorOnClassMethod8.js index bf8c06d9b90f6..c1ad826cab0ef 100644 --- a/tests/baselines/reference/decoratorOnClassMethod8.js +++ b/tests/baselines/reference/decoratorOnClassMethod8.js @@ -16,8 +16,8 @@ var C = (function () { function C() { } C.prototype.method = function () { }; - __decorate([ - dec - ], C.prototype, "method", null); return C; }()); +__decorate([ + dec +], C.prototype, "method", null); diff --git a/tests/baselines/reference/decoratorOnClassMethodOverload2.js b/tests/baselines/reference/decoratorOnClassMethodOverload2.js index eafa5da711097..db077dd7b516b 100644 --- a/tests/baselines/reference/decoratorOnClassMethodOverload2.js +++ b/tests/baselines/reference/decoratorOnClassMethodOverload2.js @@ -18,8 +18,8 @@ var C = (function () { function C() { } C.prototype.method = function () { }; - __decorate([ - dec - ], C.prototype, "method", null); return C; }()); +__decorate([ + dec +], C.prototype, "method", null); diff --git a/tests/baselines/reference/decoratorOnClassMethodParameter1.js b/tests/baselines/reference/decoratorOnClassMethodParameter1.js index efb1e4abff2cb..d5d8599d7a853 100644 --- a/tests/baselines/reference/decoratorOnClassMethodParameter1.js +++ b/tests/baselines/reference/decoratorOnClassMethodParameter1.js @@ -19,8 +19,8 @@ var C = (function () { function C() { } C.prototype.method = function (p) { }; - __decorate([ - __param(0, dec) - ], C.prototype, "method", null); return C; }()); +__decorate([ + __param(0, dec) +], C.prototype, "method", null); diff --git a/tests/baselines/reference/decoratorOnClassProperty1.js b/tests/baselines/reference/decoratorOnClassProperty1.js index 65a399332f161..6547e0bdb1873 100644 --- a/tests/baselines/reference/decoratorOnClassProperty1.js +++ b/tests/baselines/reference/decoratorOnClassProperty1.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - __decorate([ - dec - ], C.prototype, "prop", void 0); return C; }()); +__decorate([ + dec +], C.prototype, "prop", void 0); diff --git a/tests/baselines/reference/decoratorOnClassProperty10.js b/tests/baselines/reference/decoratorOnClassProperty10.js index 9df40eaf83a38..174f26a315a72 100644 --- a/tests/baselines/reference/decoratorOnClassProperty10.js +++ b/tests/baselines/reference/decoratorOnClassProperty10.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - __decorate([ - dec() - ], C.prototype, "prop", void 0); return C; }()); +__decorate([ + dec() +], C.prototype, "prop", void 0); diff --git a/tests/baselines/reference/decoratorOnClassProperty11.js b/tests/baselines/reference/decoratorOnClassProperty11.js index 8b771caf8b515..c901637402d70 100644 --- a/tests/baselines/reference/decoratorOnClassProperty11.js +++ b/tests/baselines/reference/decoratorOnClassProperty11.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - __decorate([ - dec - ], C.prototype, "prop", void 0); return C; }()); +__decorate([ + dec +], C.prototype, "prop", void 0); diff --git a/tests/baselines/reference/decoratorOnClassProperty2.js b/tests/baselines/reference/decoratorOnClassProperty2.js index 3ab2b515e3c02..ceaf43caa59cf 100644 --- a/tests/baselines/reference/decoratorOnClassProperty2.js +++ b/tests/baselines/reference/decoratorOnClassProperty2.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - __decorate([ - dec - ], C.prototype, "prop", void 0); return C; }()); +__decorate([ + dec +], C.prototype, "prop", void 0); diff --git a/tests/baselines/reference/decoratorOnClassProperty3.js b/tests/baselines/reference/decoratorOnClassProperty3.js index 9c0d3f90e42f2..8436be9396853 100644 --- a/tests/baselines/reference/decoratorOnClassProperty3.js +++ b/tests/baselines/reference/decoratorOnClassProperty3.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - __decorate([ - dec - ], C.prototype, "prop", void 0); return C; }()); +__decorate([ + dec +], C.prototype, "prop", void 0); diff --git a/tests/baselines/reference/decoratorOnClassProperty6.js b/tests/baselines/reference/decoratorOnClassProperty6.js index 823a652af2404..c7f5490c64869 100644 --- a/tests/baselines/reference/decoratorOnClassProperty6.js +++ b/tests/baselines/reference/decoratorOnClassProperty6.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - __decorate([ - dec - ], C.prototype, "prop", void 0); return C; }()); +__decorate([ + dec +], C.prototype, "prop", void 0); diff --git a/tests/baselines/reference/decoratorOnClassProperty7.js b/tests/baselines/reference/decoratorOnClassProperty7.js index 134f35022e04a..828d548ff6b29 100644 --- a/tests/baselines/reference/decoratorOnClassProperty7.js +++ b/tests/baselines/reference/decoratorOnClassProperty7.js @@ -15,8 +15,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var C = (function () { function C() { } - __decorate([ - dec - ], C.prototype, "prop", void 0); return C; }()); +__decorate([ + dec +], C.prototype, "prop", void 0); diff --git a/tests/baselines/reference/derivedClassSuperCallsInNonConstructorMembers.js b/tests/baselines/reference/derivedClassSuperCallsInNonConstructorMembers.js index e043ee8a5c53e..50408d3bee764 100644 --- a/tests/baselines/reference/derivedClassSuperCallsInNonConstructorMembers.js +++ b/tests/baselines/reference/derivedClassSuperCallsInNonConstructorMembers.js @@ -77,6 +77,6 @@ var Derived = (function (_super) { enumerable: true, configurable: true }); - Derived.a = _super.call(this); return Derived; }(Base)); +Derived.a = _super.call(this); diff --git a/tests/baselines/reference/errorSuperCalls.js b/tests/baselines/reference/errorSuperCalls.js index 4cc6a4d968fe3..097b16b30242b 100644 --- a/tests/baselines/reference/errorSuperCalls.js +++ b/tests/baselines/reference/errorSuperCalls.js @@ -119,10 +119,10 @@ var NoBase = (function () { enumerable: true, configurable: true }); - //super call in static class member initializer with no base type - NoBase.k = _super.call(this); return NoBase; }()); +//super call in static class member initializer with no base type +NoBase.k = _super.call(this); var Base = (function () { function Base() { } diff --git a/tests/baselines/reference/errorSuperPropertyAccess.js b/tests/baselines/reference/errorSuperPropertyAccess.js index cf6ed2598eade..dbfbdb4694023 100644 --- a/tests/baselines/reference/errorSuperPropertyAccess.js +++ b/tests/baselines/reference/errorSuperPropertyAccess.js @@ -175,10 +175,10 @@ var SomeBase = (function () { SomeBase.prototype.publicFunc = function () { }; SomeBase.privateStaticFunc = function () { }; SomeBase.publicStaticFunc = function () { }; - SomeBase.privateStaticMember = 0; - SomeBase.publicStaticMember = 0; return SomeBase; }()); +SomeBase.privateStaticMember = 0; +SomeBase.publicStaticMember = 0; //super.publicInstanceMemberNotFunction in constructor of derived class //super.publicInstanceMemberNotFunction in instance member function of derived class //super.publicInstanceMemberNotFunction in instance member accessor(get and set) of derived class diff --git a/tests/baselines/reference/es3defaultAliasIsQuoted.js b/tests/baselines/reference/es3defaultAliasIsQuoted.js index ac6a0bd24daab..89fd0c47a7e94 100644 --- a/tests/baselines/reference/es3defaultAliasIsQuoted.js +++ b/tests/baselines/reference/es3defaultAliasIsQuoted.js @@ -19,9 +19,9 @@ assert(Foo.CONSTANT === "Foo"); var Foo = (function () { function Foo() { } - Foo.CONSTANT = "Foo"; return Foo; }()); +Foo.CONSTANT = "Foo"; exports.Foo = Foo; function assert(value) { if (!value) diff --git a/tests/baselines/reference/es6ClassTest.js b/tests/baselines/reference/es6ClassTest.js index 365e91321a451..a722cce167033 100644 --- a/tests/baselines/reference/es6ClassTest.js +++ b/tests/baselines/reference/es6ClassTest.js @@ -113,9 +113,9 @@ var Foo = (function (_super) { } Foo.prototype.bar = function () { return 0; }; Foo.prototype.boo = function (x) { return x; }; - Foo.statVal = 0; return Foo; }(Bar)); +Foo.statVal = 0; var f = new Foo(); //class GetSetMonster { // // attack(target) { diff --git a/tests/baselines/reference/es6ClassTest2.js b/tests/baselines/reference/es6ClassTest2.js index 86ff00121cf15..1881e6b8c878c 100644 --- a/tests/baselines/reference/es6ClassTest2.js +++ b/tests/baselines/reference/es6ClassTest2.js @@ -283,9 +283,9 @@ var Statics = (function () { Statics.baz = function () { return ""; }; - Statics.foo = 1; return Statics; }()); +Statics.foo = 1; var stat = new Statics(); var ImplementsInterface = (function () { function ImplementsInterface() { diff --git a/tests/baselines/reference/generatedContextualTyping.js b/tests/baselines/reference/generatedContextualTyping.js index 8093c8bec4195..e5488121d34eb 100644 --- a/tests/baselines/reference/generatedContextualTyping.js +++ b/tests/baselines/reference/generatedContextualTyping.js @@ -612,219 +612,219 @@ var x48 = (function () { var x49 = (function () { function x49() { } - x49.member = function () { return [d1, d2]; }; return x49; }()); +x49.member = function () { return [d1, d2]; }; var x50 = (function () { function x50() { } - x50.member = function () { return [d1, d2]; }; return x50; }()); +x50.member = function () { return [d1, d2]; }; var x51 = (function () { function x51() { } - x51.member = function named() { return [d1, d2]; }; return x51; }()); +x51.member = function named() { return [d1, d2]; }; var x52 = (function () { function x52() { } - x52.member = function () { return [d1, d2]; }; return x52; }()); +x52.member = function () { return [d1, d2]; }; var x53 = (function () { function x53() { } - x53.member = function () { return [d1, d2]; }; return x53; }()); +x53.member = function () { return [d1, d2]; }; var x54 = (function () { function x54() { } - x54.member = function named() { return [d1, d2]; }; return x54; }()); +x54.member = function named() { return [d1, d2]; }; var x55 = (function () { function x55() { } - x55.member = [d1, d2]; return x55; }()); +x55.member = [d1, d2]; var x56 = (function () { function x56() { } - x56.member = [d1, d2]; return x56; }()); +x56.member = [d1, d2]; var x57 = (function () { function x57() { } - x57.member = [d1, d2]; return x57; }()); +x57.member = [d1, d2]; var x58 = (function () { function x58() { } - x58.member = { n: [d1, d2] }; return x58; }()); +x58.member = { n: [d1, d2] }; var x59 = (function () { function x59() { } - x59.member = function (n) { var n; return null; }; return x59; }()); +x59.member = function (n) { var n; return null; }; var x60 = (function () { function x60() { } - x60.member = { func: function (n) { return [d1, d2]; } }; return x60; }()); +x60.member = { func: function (n) { return [d1, d2]; } }; var x61 = (function () { function x61() { } - x61.member = function () { return [d1, d2]; }; return x61; }()); +x61.member = function () { return [d1, d2]; }; var x62 = (function () { function x62() { } - x62.member = function () { return [d1, d2]; }; return x62; }()); +x62.member = function () { return [d1, d2]; }; var x63 = (function () { function x63() { } - x63.member = function named() { return [d1, d2]; }; return x63; }()); +x63.member = function named() { return [d1, d2]; }; var x64 = (function () { function x64() { } - x64.member = function () { return [d1, d2]; }; return x64; }()); +x64.member = function () { return [d1, d2]; }; var x65 = (function () { function x65() { } - x65.member = function () { return [d1, d2]; }; return x65; }()); +x65.member = function () { return [d1, d2]; }; var x66 = (function () { function x66() { } - x66.member = function named() { return [d1, d2]; }; return x66; }()); +x66.member = function named() { return [d1, d2]; }; var x67 = (function () { function x67() { } - x67.member = [d1, d2]; return x67; }()); +x67.member = [d1, d2]; var x68 = (function () { function x68() { } - x68.member = [d1, d2]; return x68; }()); +x68.member = [d1, d2]; var x69 = (function () { function x69() { } - x69.member = [d1, d2]; return x69; }()); +x69.member = [d1, d2]; var x70 = (function () { function x70() { } - x70.member = { n: [d1, d2] }; return x70; }()); +x70.member = { n: [d1, d2] }; var x71 = (function () { function x71() { } - x71.member = function (n) { var n; return null; }; return x71; }()); +x71.member = function (n) { var n; return null; }; var x72 = (function () { function x72() { } - x72.member = { func: function (n) { return [d1, d2]; } }; return x72; }()); +x72.member = { func: function (n) { return [d1, d2]; } }; var x73 = (function () { function x73() { } - x73.member = function () { return [d1, d2]; }; return x73; }()); +x73.member = function () { return [d1, d2]; }; var x74 = (function () { function x74() { } - x74.member = function () { return [d1, d2]; }; return x74; }()); +x74.member = function () { return [d1, d2]; }; var x75 = (function () { function x75() { } - x75.member = function named() { return [d1, d2]; }; return x75; }()); +x75.member = function named() { return [d1, d2]; }; var x76 = (function () { function x76() { } - x76.member = function () { return [d1, d2]; }; return x76; }()); +x76.member = function () { return [d1, d2]; }; var x77 = (function () { function x77() { } - x77.member = function () { return [d1, d2]; }; return x77; }()); +x77.member = function () { return [d1, d2]; }; var x78 = (function () { function x78() { } - x78.member = function named() { return [d1, d2]; }; return x78; }()); +x78.member = function named() { return [d1, d2]; }; var x79 = (function () { function x79() { } - x79.member = [d1, d2]; return x79; }()); +x79.member = [d1, d2]; var x80 = (function () { function x80() { } - x80.member = [d1, d2]; return x80; }()); +x80.member = [d1, d2]; var x81 = (function () { function x81() { } - x81.member = [d1, d2]; return x81; }()); +x81.member = [d1, d2]; var x82 = (function () { function x82() { } - x82.member = { n: [d1, d2] }; return x82; }()); +x82.member = { n: [d1, d2] }; var x83 = (function () { function x83() { } - x83.member = function (n) { var n; return null; }; return x83; }()); +x83.member = function (n) { var n; return null; }; var x84 = (function () { function x84() { } - x84.member = { func: function (n) { return [d1, d2]; } }; return x84; }()); +x84.member = { func: function (n) { return [d1, d2]; } }; var x85 = (function () { function x85(parm) { if (parm === void 0) { parm = function () { return [d1, d2]; }; } diff --git a/tests/baselines/reference/genericClassWithStaticsUsingTypeArguments.js b/tests/baselines/reference/genericClassWithStaticsUsingTypeArguments.js index 2039e459572ac..053fa785ee01a 100644 --- a/tests/baselines/reference/genericClassWithStaticsUsingTypeArguments.js +++ b/tests/baselines/reference/genericClassWithStaticsUsingTypeArguments.js @@ -25,9 +25,9 @@ var Foo = (function () { Foo.f = function (xs) { return xs.reverse(); }; - Foo.a = function (n) { }; - Foo.c = []; - Foo.d = false || (function (x) { return x || undefined; })(null); - Foo.e = function (x) { return null; }; return Foo; }()); +Foo.a = function (n) { }; +Foo.c = []; +Foo.d = false || (function (x) { return x || undefined; })(null); +Foo.e = function (x) { return null; }; diff --git a/tests/baselines/reference/gettersAndSetters.js b/tests/baselines/reference/gettersAndSetters.js index 16d66c7dc71f6..265a1210b16f4 100644 --- a/tests/baselines/reference/gettersAndSetters.js +++ b/tests/baselines/reference/gettersAndSetters.js @@ -65,9 +65,9 @@ var C = (function () { enumerable: true, configurable: true }); - C.barBack = ""; return C; }()); +C.barBack = ""; var c = new C(); var foo = c.Foo; c.Foo = "foov"; diff --git a/tests/baselines/reference/importImportOnlyModule.js b/tests/baselines/reference/importImportOnlyModule.js index 7b41e4400c5b0..013999321f959 100644 --- a/tests/baselines/reference/importImportOnlyModule.js +++ b/tests/baselines/reference/importImportOnlyModule.js @@ -22,9 +22,9 @@ define(["require", "exports"], function (require, exports) { function C1() { this.m1 = 42; } - C1.s1 = true; return C1; }()); + C1.s1 = true; exports.C1 = C1; }); //// [foo_1.js] diff --git a/tests/baselines/reference/instanceAndStaticDeclarations1.js b/tests/baselines/reference/instanceAndStaticDeclarations1.js index 093e9c15cc8ea..bf9b6963bb2f1 100644 --- a/tests/baselines/reference/instanceAndStaticDeclarations1.js +++ b/tests/baselines/reference/instanceAndStaticDeclarations1.js @@ -25,6 +25,6 @@ var Point = (function () { return Math.sqrt(dx * dx + dy * dy); }; Point.distance = function (p1, p2) { return p1.distance(p2); }; - Point.origin = new Point(0, 0); return Point; }()); +Point.origin = new Point(0, 0); diff --git a/tests/baselines/reference/invalidStaticField.js b/tests/baselines/reference/invalidStaticField.js index dc582e830c7c7..08a06d5ee27e0 100644 --- a/tests/baselines/reference/invalidStaticField.js +++ b/tests/baselines/reference/invalidStaticField.js @@ -12,6 +12,6 @@ var A = (function () { var B = (function () { function B() { } - B.NOT_NULL = new B(); return B; }()); +B.NOT_NULL = new B(); diff --git a/tests/baselines/reference/literalsInComputedProperties1.js b/tests/baselines/reference/literalsInComputedProperties1.js index 82a6367e5d274..4c28a5e908387 100644 --- a/tests/baselines/reference/literalsInComputedProperties1.js +++ b/tests/baselines/reference/literalsInComputedProperties1.js @@ -58,8 +58,7 @@ var x = (_a = { _a[2] = 1, _a["3"] = 1, _a["4"] = 1, - _a -); + _a); x[1].toExponential(); x[2].toExponential(); x[3].toExponential(); diff --git a/tests/baselines/reference/missingDecoratorType.js b/tests/baselines/reference/missingDecoratorType.js index 7356be39798a1..1dcbb3febc5e9 100644 --- a/tests/baselines/reference/missingDecoratorType.js +++ b/tests/baselines/reference/missingDecoratorType.js @@ -33,8 +33,8 @@ var C = (function () { function C() { } C.prototype.method = function () { }; - __decorate([ - dec - ], C.prototype, "method", null); return C; }()); +__decorate([ + dec +], C.prototype, "method", null); diff --git a/tests/baselines/reference/modifierOnClassExpressionMemberInFunction.js b/tests/baselines/reference/modifierOnClassExpressionMemberInFunction.js index 70eea5b2c5c68..211c80bcce853 100644 --- a/tests/baselines/reference/modifierOnClassExpressionMemberInFunction.js +++ b/tests/baselines/reference/modifierOnClassExpressionMemberInFunction.js @@ -10,14 +10,16 @@ function g() { //// [modifierOnClassExpressionMemberInFunction.js] function g() { - var x = (function () { - function C() { - this.prop1 = 1; - } - C.prototype.foo = function () { }; - C.prop2 = 43; - return C; - }()); + var x = (_a = (function () { + function C() { + this.prop1 = 1; + } + C.prototype.foo = function () { }; + return C; + }()), + _a.prop2 = 43, + _a); + var _a; } diff --git a/tests/baselines/reference/noEmitHelpers2.js b/tests/baselines/reference/noEmitHelpers2.js index f3bbfe54896d8..fc4baf334c3d7 100644 --- a/tests/baselines/reference/noEmitHelpers2.js +++ b/tests/baselines/reference/noEmitHelpers2.js @@ -12,10 +12,10 @@ class A { var A = (function () { function A(a, b) { } - A = __decorate([ - decorator, - __param(1, decorator), - __metadata('design:paramtypes', [Number, String]) - ], A); return A; }()); +A = __decorate([ + decorator, + __param(1, decorator), + __metadata('design:paramtypes', [Number, String]) +], A); diff --git a/tests/baselines/reference/parserAccessibilityAfterStatic3.js b/tests/baselines/reference/parserAccessibilityAfterStatic3.js index 7a9ae06e43929..38202bc5dc8fe 100644 --- a/tests/baselines/reference/parserAccessibilityAfterStatic3.js +++ b/tests/baselines/reference/parserAccessibilityAfterStatic3.js @@ -9,6 +9,6 @@ static public = 1; var Outer = (function () { function Outer() { } - Outer.public = 1; return Outer; }()); +Outer.public = 1; diff --git a/tests/baselines/reference/parserErrorRecovery_IncompleteMemberVariable1.js b/tests/baselines/reference/parserErrorRecovery_IncompleteMemberVariable1.js index 9bb4395e55655..57d0536e97597 100644 --- a/tests/baselines/reference/parserErrorRecovery_IncompleteMemberVariable1.js +++ b/tests/baselines/reference/parserErrorRecovery_IncompleteMemberVariable1.js @@ -41,10 +41,10 @@ var Shapes; } // Instance member Point.prototype.getDist = function () { return Math.sqrt(this.x * this.x + this.y * this.y); }; - // Static member - Point.origin = new Point(0, 0); return Point; }()); + // Static member + Point.origin = new Point(0, 0); Shapes.Point = Point; })(Shapes || (Shapes = {})); // Local variables diff --git a/tests/baselines/reference/parserErrorRecovery_IncompleteMemberVariable2.js b/tests/baselines/reference/parserErrorRecovery_IncompleteMemberVariable2.js index 0ee73b8b1e5bf..2bb08a14e350b 100644 --- a/tests/baselines/reference/parserErrorRecovery_IncompleteMemberVariable2.js +++ b/tests/baselines/reference/parserErrorRecovery_IncompleteMemberVariable2.js @@ -42,10 +42,10 @@ var Shapes; } // Instance member Point.prototype.getDist = function () { return Math.sqrt(this.x * this.x + this.y * this.y); }; - // Static member - Point.origin = new Point(0, 0); return Point; }()); + // Static member + Point.origin = new Point(0, 0); Shapes.Point = Point; })(Shapes || (Shapes = {})); // Local variables diff --git a/tests/baselines/reference/parserharness.js b/tests/baselines/reference/parserharness.js index 6b38a8c9e4cc7..1a525c49d197f 100644 --- a/tests/baselines/reference/parserharness.js +++ b/tests/baselines/reference/parserharness.js @@ -2371,11 +2371,11 @@ var Harness; errorHandlerStack[errorHandlerStack.length - 1](e); } }; - // The current stack of Runnable objects - Runnable.currentStack = []; - Runnable.errorHandlerStack = []; return Runnable; }()); + // The current stack of Runnable objects + Runnable.currentStack = []; + Runnable.errorHandlerStack = []; Harness.Runnable = Runnable; var TestCase = (function (_super) { __extends(TestCase, _super); diff --git a/tests/baselines/reference/privacyCannotNameVarTypeDeclFile.js b/tests/baselines/reference/privacyCannotNameVarTypeDeclFile.js index 6c2dcf304cfb7..c139b6279fd1d 100644 --- a/tests/baselines/reference/privacyCannotNameVarTypeDeclFile.js +++ b/tests/baselines/reference/privacyCannotNameVarTypeDeclFile.js @@ -159,12 +159,12 @@ var publicClassWithWithPrivatePropertyTypes = (function () { this.myPublicProperty1 = exporter.createExportedWidget3(); // Error this.myPrivateProperty1 = exporter.createExportedWidget3(); } - publicClassWithWithPrivatePropertyTypes.myPublicStaticProperty = exporter.createExportedWidget1(); // Error - publicClassWithWithPrivatePropertyTypes.myPrivateStaticProperty = exporter.createExportedWidget1(); - publicClassWithWithPrivatePropertyTypes.myPublicStaticProperty1 = exporter.createExportedWidget3(); // Error - publicClassWithWithPrivatePropertyTypes.myPrivateStaticProperty1 = exporter.createExportedWidget3(); return publicClassWithWithPrivatePropertyTypes; }()); +publicClassWithWithPrivatePropertyTypes.myPublicStaticProperty = exporter.createExportedWidget1(); // Error +publicClassWithWithPrivatePropertyTypes.myPrivateStaticProperty = exporter.createExportedWidget1(); +publicClassWithWithPrivatePropertyTypes.myPublicStaticProperty1 = exporter.createExportedWidget3(); // Error +publicClassWithWithPrivatePropertyTypes.myPrivateStaticProperty1 = exporter.createExportedWidget3(); exports.publicClassWithWithPrivatePropertyTypes = publicClassWithWithPrivatePropertyTypes; var privateClassWithWithPrivatePropertyTypes = (function () { function privateClassWithWithPrivatePropertyTypes() { @@ -173,12 +173,12 @@ var privateClassWithWithPrivatePropertyTypes = (function () { this.myPublicProperty1 = exporter.createExportedWidget3(); this.myPrivateProperty1 = exporter.createExportedWidget3(); } - privateClassWithWithPrivatePropertyTypes.myPublicStaticProperty = exporter.createExportedWidget1(); - privateClassWithWithPrivatePropertyTypes.myPrivateStaticProperty = exporter.createExportedWidget1(); - privateClassWithWithPrivatePropertyTypes.myPublicStaticProperty1 = exporter.createExportedWidget3(); - privateClassWithWithPrivatePropertyTypes.myPrivateStaticProperty1 = exporter.createExportedWidget3(); return privateClassWithWithPrivatePropertyTypes; }()); +privateClassWithWithPrivatePropertyTypes.myPublicStaticProperty = exporter.createExportedWidget1(); +privateClassWithWithPrivatePropertyTypes.myPrivateStaticProperty = exporter.createExportedWidget1(); +privateClassWithWithPrivatePropertyTypes.myPublicStaticProperty1 = exporter.createExportedWidget3(); +privateClassWithWithPrivatePropertyTypes.myPrivateStaticProperty1 = exporter.createExportedWidget3(); exports.publicVarWithPrivatePropertyTypes = exporter.createExportedWidget1(); // Error var privateVarWithPrivatePropertyTypes = exporter.createExportedWidget1(); exports.publicVarWithPrivatePropertyTypes1 = exporter.createExportedWidget3(); // Error @@ -188,10 +188,10 @@ var publicClassWithPrivateModulePropertyTypes = (function () { this.myPublicProperty = exporter.createExportedWidget2(); // Error this.myPublicProperty1 = exporter.createExportedWidget4(); // Error } - publicClassWithPrivateModulePropertyTypes.myPublicStaticProperty = exporter.createExportedWidget2(); // Error - publicClassWithPrivateModulePropertyTypes.myPublicStaticProperty1 = exporter.createExportedWidget4(); // Error return publicClassWithPrivateModulePropertyTypes; }()); +publicClassWithPrivateModulePropertyTypes.myPublicStaticProperty = exporter.createExportedWidget2(); // Error +publicClassWithPrivateModulePropertyTypes.myPublicStaticProperty1 = exporter.createExportedWidget4(); // Error exports.publicClassWithPrivateModulePropertyTypes = publicClassWithPrivateModulePropertyTypes; exports.publicVarWithPrivateModulePropertyTypes = exporter.createExportedWidget2(); // Error exports.publicVarWithPrivateModulePropertyTypes1 = exporter.createExportedWidget4(); // Error @@ -200,10 +200,10 @@ var privateClassWithPrivateModulePropertyTypes = (function () { this.myPublicProperty = exporter.createExportedWidget2(); this.myPublicProperty1 = exporter.createExportedWidget4(); } - privateClassWithPrivateModulePropertyTypes.myPublicStaticProperty = exporter.createExportedWidget2(); - privateClassWithPrivateModulePropertyTypes.myPublicStaticProperty1 = exporter.createExportedWidget4(); return privateClassWithPrivateModulePropertyTypes; }()); +privateClassWithPrivateModulePropertyTypes.myPublicStaticProperty = exporter.createExportedWidget2(); +privateClassWithPrivateModulePropertyTypes.myPublicStaticProperty1 = exporter.createExportedWidget4(); var privateVarWithPrivateModulePropertyTypes = exporter.createExportedWidget2(); var privateVarWithPrivateModulePropertyTypes1 = exporter.createExportedWidget4(); diff --git a/tests/baselines/reference/privateIndexer2.js b/tests/baselines/reference/privateIndexer2.js index 1268c6e4c25a2..a90e03a5bc2bd 100644 --- a/tests/baselines/reference/privateIndexer2.js +++ b/tests/baselines/reference/privateIndexer2.js @@ -14,7 +14,6 @@ var y: { var x = (_a = {}, _a[x] = string, _a.string = , - _a -); + _a); var y; var _a; diff --git a/tests/baselines/reference/privateStaticMemberAccessibility.js b/tests/baselines/reference/privateStaticMemberAccessibility.js index 2074e5aa8649e..352256ac9bd66 100644 --- a/tests/baselines/reference/privateStaticMemberAccessibility.js +++ b/tests/baselines/reference/privateStaticMemberAccessibility.js @@ -25,6 +25,6 @@ var Derived = (function (_super) { _super.apply(this, arguments); this.bing = function () { return Base.foo; }; // error } - Derived.bar = Base.foo; // error return Derived; }(Base)); +Derived.bar = Base.foo; // error diff --git a/tests/baselines/reference/propertyAccessibility2.js b/tests/baselines/reference/propertyAccessibility2.js index afce6c4c24a79..3618b86fcc5b4 100644 --- a/tests/baselines/reference/propertyAccessibility2.js +++ b/tests/baselines/reference/propertyAccessibility2.js @@ -9,7 +9,7 @@ var c = C.x; var C = (function () { function C() { } - C.x = 1; return C; }()); +C.x = 1; var c = C.x; diff --git a/tests/baselines/reference/quotedPropertyName2.js b/tests/baselines/reference/quotedPropertyName2.js index 1c4d85076eac1..550e52997027f 100644 --- a/tests/baselines/reference/quotedPropertyName2.js +++ b/tests/baselines/reference/quotedPropertyName2.js @@ -7,6 +7,6 @@ class Test1 { var Test1 = (function () { function Test1() { } - Test1["prop1"] = 0; return Test1; }()); +Test1["prop1"] = 0; diff --git a/tests/baselines/reference/reassignStaticProp.js b/tests/baselines/reference/reassignStaticProp.js index 2bf2bafa128e2..d9c37c29ad5b6 100644 --- a/tests/baselines/reference/reassignStaticProp.js +++ b/tests/baselines/reference/reassignStaticProp.js @@ -15,6 +15,6 @@ class foo { var foo = (function () { function foo() { } - foo.bar = 1; return foo; }()); +foo.bar = 1; diff --git a/tests/baselines/reference/sourceMap-FileWithComments.js b/tests/baselines/reference/sourceMap-FileWithComments.js index 9bb18dba33d5f..dd2131889afcc 100644 --- a/tests/baselines/reference/sourceMap-FileWithComments.js +++ b/tests/baselines/reference/sourceMap-FileWithComments.js @@ -49,10 +49,10 @@ var Shapes; } // Instance member Point.prototype.getDist = function () { return Math.sqrt(this.x * this.x + this.y * this.y); }; - // Static member - Point.origin = new Point(0, 0); return Point; }()); + // Static member + Point.origin = new Point(0, 0); Shapes.Point = Point; // Variable comment after class var a = 10; diff --git a/tests/baselines/reference/sourceMap-FileWithComments.js.map b/tests/baselines/reference/sourceMap-FileWithComments.js.map index f3e9674336df7..80437501d33a3 100644 --- a/tests/baselines/reference/sourceMap-FileWithComments.js.map +++ b/tests/baselines/reference/sourceMap-FileWithComments.js.map @@ -1,2 +1,2 @@ //// [sourceMap-FileWithComments.js.map] -{"version":3,"file":"sourceMap-FileWithComments.js","sourceRoot":"","sources":["sourceMap-FileWithComments.ts"],"names":[],"mappings":"AAMA,SAAS;AACT,IAAO,MAAM,CAwBZ;AAxBD,WAAO,MAAM,EAAC,CAAC;IAEX,QAAQ;IACR;QACI,cAAc;QACd,eAAmB,CAAS,EAAS,CAAS;YAA3B,MAAC,GAAD,CAAC,CAAQ;YAAS,MAAC,GAAD,CAAC,CAAQ;QAAI,CAAC;QAEnD,kBAAkB;QAClB,uBAAO,GAAP,cAAY,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAElE,gBAAgB;QACT,YAAM,GAAG,IAAI,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACpC,YAAC;IAAD,CAAC,AATD,IASC;IATY,YAAK,QASjB,CAAA;IAED,+BAA+B;IAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;IAEX;IACA,CAAC;IADe,UAAG,MAClB,CAAA;IAED;;MAEE;IACF,IAAI,CAAC,GAAG,EAAE,CAAC;AACf,CAAC,EAxBM,MAAM,KAAN,MAAM,QAwBZ;AAED,qBAAqB;AACrB,IAAI,CAAC,GAAW,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACvC,IAAI,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"sourceMap-FileWithComments.js","sourceRoot":"","sources":["sourceMap-FileWithComments.ts"],"names":[],"mappings":"AAMA,SAAS;AACT,IAAO,MAAM,CAwBZ;AAxBD,WAAO,MAAM,EAAC,CAAC;IAEX,QAAQ;IACR;QACI,cAAc;QACd,eAAmB,CAAS,EAAS,CAAS;YAA3B,MAAC,GAAD,CAAC,CAAQ;YAAS,MAAC,GAAD,CAAC,CAAQ;QAAI,CAAC;QAEnD,kBAAkB;QAClB,uBAAO,GAAP,cAAY,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAItE,YAAC;IAAD,CAAC,AATD;IAOI,gBAAgB;IACT,YAAM,GAAG,IAAI,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAClC;IATY,YAAK,QASjB,CAAA;IAED,+BAA+B;IAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;IAEX;IACA,CAAC;IADe,UAAG,MAClB,CAAA;IAED;;MAEE;IACF,IAAI,CAAC,GAAG,EAAE,CAAC;AACf,CAAC,EAxBM,MAAM,KAAN,MAAM,QAwBZ;AAED,qBAAqB;AACrB,IAAI,CAAC,GAAW,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACvC,IAAI,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/sourceMap-FileWithComments.sourcemap.txt b/tests/baselines/reference/sourceMap-FileWithComments.sourcemap.txt index 3bcf26345079d..2abfabff566b6 100644 --- a/tests/baselines/reference/sourceMap-FileWithComments.sourcemap.txt +++ b/tests/baselines/reference/sourceMap-FileWithComments.sourcemap.txt @@ -271,93 +271,90 @@ sourceFile:sourceMap-FileWithComments.ts 28>Emitted(12, 102) Source(16, 74) + SourceIndex(0) 29>Emitted(12, 103) Source(16, 75) + SourceIndex(0) --- ->>> // Static member -1 >^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^ -3 > ^^^^^^^^^^^^^^^^-> -1 > - > - > -2 > // Static member -1 >Emitted(13, 9) Source(18, 9) + SourceIndex(0) -2 >Emitted(13, 25) Source(18, 25) + SourceIndex(0) ---- ->>> Point.origin = new Point(0, 0); -1->^^^^^^^^ -2 > ^^^^^^^^^^^^ -3 > ^^^ -4 > ^^^^ -5 > ^^^^^ -6 > ^ -7 > ^ -8 > ^^ -9 > ^ -10> ^ -11> ^ -1-> - > static -2 > origin -3 > = -4 > new -5 > Point -6 > ( -7 > 0 -8 > , -9 > 0 -10> ) -11> ; -1->Emitted(14, 9) Source(19, 16) + SourceIndex(0) -2 >Emitted(14, 21) Source(19, 22) + SourceIndex(0) -3 >Emitted(14, 24) Source(19, 25) + SourceIndex(0) -4 >Emitted(14, 28) Source(19, 29) + SourceIndex(0) -5 >Emitted(14, 33) Source(19, 34) + SourceIndex(0) -6 >Emitted(14, 34) Source(19, 35) + SourceIndex(0) -7 >Emitted(14, 35) Source(19, 36) + SourceIndex(0) -8 >Emitted(14, 37) Source(19, 38) + SourceIndex(0) -9 >Emitted(14, 38) Source(19, 39) + SourceIndex(0) -10>Emitted(14, 39) Source(19, 40) + SourceIndex(0) -11>Emitted(14, 40) Source(19, 41) + SourceIndex(0) ---- >>> return Point; 1 >^^^^^^^^ 2 > ^^^^^^^^^^^^ 1 > + > + > // Static member + > static origin = new Point(0, 0); > 2 > } -1 >Emitted(15, 9) Source(20, 5) + SourceIndex(0) -2 >Emitted(15, 21) Source(20, 6) + SourceIndex(0) +1 >Emitted(13, 9) Source(20, 5) + SourceIndex(0) +2 >Emitted(13, 21) Source(20, 6) + SourceIndex(0) --- >>> }()); 1 >^^^^ 2 > ^ 3 > -4 > ^^^^ -5 > ^^^^^^^^^^^^^^^^^-> +4 > ^^^^^^^^^^^^^^^^-> 1 > 2 > } 3 > -4 > export class Point implements IPoint { - > // Constructor - > constructor(public x: number, public y: number) { } - > - > // Instance member - > getDist() { return Math.sqrt(this.x * this.x + this.y * this.y); } - > - > // Static member - > static origin = new Point(0, 0); - > } -1 >Emitted(16, 5) Source(20, 5) + SourceIndex(0) -2 >Emitted(16, 6) Source(20, 6) + SourceIndex(0) -3 >Emitted(16, 6) Source(11, 5) + SourceIndex(0) -4 >Emitted(16, 10) Source(20, 6) + SourceIndex(0) +1 >Emitted(14, 5) Source(20, 5) + SourceIndex(0) +2 >Emitted(14, 6) Source(20, 6) + SourceIndex(0) +3 >Emitted(14, 6) Source(11, 5) + SourceIndex(0) --- ->>> Shapes.Point = Point; +>>> // Static member +1->^^^^ +2 > ^^^^^^^^^^^^^^^^ +3 > ^^^^^^^^^^^^^^^^-> +1->export class Point implements IPoint { + > // Constructor + > constructor(public x: number, public y: number) { } + > + > // Instance member + > getDist() { return Math.sqrt(this.x * this.x + this.y * this.y); } + > + > +2 > // Static member +1->Emitted(15, 5) Source(18, 9) + SourceIndex(0) +2 >Emitted(15, 21) Source(18, 25) + SourceIndex(0) +--- +>>> Point.origin = new Point(0, 0); 1->^^^^ 2 > ^^^^^^^^^^^^ +3 > ^^^ +4 > ^^^^ +5 > ^^^^^ +6 > ^ +7 > ^ +8 > ^^ +9 > ^ +10> ^ +11> ^ +1-> + > static +2 > origin +3 > = +4 > new +5 > Point +6 > ( +7 > 0 +8 > , +9 > 0 +10> ) +11> ; + > } +1->Emitted(16, 5) Source(19, 16) + SourceIndex(0) +2 >Emitted(16, 17) Source(19, 22) + SourceIndex(0) +3 >Emitted(16, 20) Source(19, 25) + SourceIndex(0) +4 >Emitted(16, 24) Source(19, 29) + SourceIndex(0) +5 >Emitted(16, 29) Source(19, 34) + SourceIndex(0) +6 >Emitted(16, 30) Source(19, 35) + SourceIndex(0) +7 >Emitted(16, 31) Source(19, 36) + SourceIndex(0) +8 >Emitted(16, 33) Source(19, 38) + SourceIndex(0) +9 >Emitted(16, 34) Source(19, 39) + SourceIndex(0) +10>Emitted(16, 35) Source(19, 40) + SourceIndex(0) +11>Emitted(16, 36) Source(20, 6) + SourceIndex(0) +--- +>>> Shapes.Point = Point; +1 >^^^^ +2 > ^^^^^^^^^^^^ 3 > ^^^^^^^^ 4 > ^ 5 > ^^^^^^^^^^^-> -1-> +1 > 2 > Point 3 > implements IPoint { > // Constructor @@ -370,7 +367,7 @@ sourceFile:sourceMap-FileWithComments.ts > static origin = new Point(0, 0); > } 4 > -1->Emitted(17, 5) Source(11, 18) + SourceIndex(0) +1 >Emitted(17, 5) Source(11, 18) + SourceIndex(0) 2 >Emitted(17, 17) Source(11, 23) + SourceIndex(0) 3 >Emitted(17, 25) Source(20, 6) + SourceIndex(0) 4 >Emitted(17, 26) Source(20, 6) + SourceIndex(0) diff --git a/tests/baselines/reference/sourceMapValidationDecorators.js b/tests/baselines/reference/sourceMapValidationDecorators.js index c3bb5cd722526..63cdb25900cb9 100644 --- a/tests/baselines/reference/sourceMapValidationDecorators.js +++ b/tests/baselines/reference/sourceMapValidationDecorators.js @@ -88,37 +88,37 @@ var Greeter = (function () { enumerable: true, configurable: true }); - Greeter.x1 = 10; - __decorate([ - PropertyDecorator1, - PropertyDecorator2(40) - ], Greeter.prototype, "greet", null); - __decorate([ - PropertyDecorator1, - PropertyDecorator2(50) - ], Greeter.prototype, "x", void 0); - __decorate([ - __param(0, ParameterDecorator1), - __param(0, ParameterDecorator2(70)) - ], Greeter.prototype, "fn", null); - __decorate([ - PropertyDecorator1, - PropertyDecorator2(80), - __param(0, ParameterDecorator1), - __param(0, ParameterDecorator2(90)) - ], Greeter.prototype, "greetings", null); - __decorate([ - PropertyDecorator1, - PropertyDecorator2(60) - ], Greeter, "x1", void 0); - Greeter = __decorate([ - ClassDecorator1, - ClassDecorator2(10), - __param(0, ParameterDecorator1), - __param(0, ParameterDecorator2(20)), - __param(1, ParameterDecorator1), - __param(1, ParameterDecorator2(30)) - ], Greeter); return Greeter; }()); +Greeter.x1 = 10; +__decorate([ + PropertyDecorator1, + PropertyDecorator2(40) +], Greeter.prototype, "greet", null); +__decorate([ + PropertyDecorator1, + PropertyDecorator2(50) +], Greeter.prototype, "x", void 0); +__decorate([ + __param(0, ParameterDecorator1), + __param(0, ParameterDecorator2(70)) +], Greeter.prototype, "fn", null); +__decorate([ + PropertyDecorator1, + PropertyDecorator2(80), + __param(0, ParameterDecorator1), + __param(0, ParameterDecorator2(90)) +], Greeter.prototype, "greetings", null); +__decorate([ + PropertyDecorator1, + PropertyDecorator2(60) +], Greeter, "x1", void 0); +Greeter = __decorate([ + ClassDecorator1, + ClassDecorator2(10), + __param(0, ParameterDecorator1), + __param(0, ParameterDecorator2(20)), + __param(1, ParameterDecorator1), + __param(1, ParameterDecorator2(30)) +], Greeter); //# sourceMappingURL=sourceMapValidationDecorators.js.map \ No newline at end of file diff --git a/tests/baselines/reference/sourceMapValidationDecorators.js.map b/tests/baselines/reference/sourceMapValidationDecorators.js.map index 0a6b45eb383d3..18b24ddf37e24 100644 --- a/tests/baselines/reference/sourceMapValidationDecorators.js.map +++ b/tests/baselines/reference/sourceMapValidationDecorators.js.map @@ -1,2 +1,2 @@ //// [sourceMapValidationDecorators.js.map] -{"version":3,"file":"sourceMapValidationDecorators.js","sourceRoot":"","sources":["sourceMapValidationDecorators.ts"],"names":[],"mappings":";;;;;;;;;AASA;IACI,iBAGS,QAAgB;QAIvB,WAAc;aAAd,WAAc,CAAd,sBAAc,CAAd,IAAc;YAAd,0BAAc;;QAJP,aAAQ,GAAR,QAAQ,CAAQ;IAKzB,CAAC;IAID,uBAAK,GAAL;QACI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC5C,CAAC;IAUO,oBAAE,GAAV,UAGE,CAAS;QACP,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAID,sBAAI,8BAAS;aAAb;YACI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;QACzB,CAAC;aAED,UAGE,SAAiB;YACf,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC9B,CAAC;;;OAPA;IAbc,UAAE,GAAW,EAAE,CAAC;IAZ/B;QAAC,kBAAkB;QAClB,kBAAkB,CAAC,EAAE,CAAC;wCAAA;IAKvB;QAAC,kBAAkB;QAClB,kBAAkB,CAAC,EAAE,CAAC;sCAAA;IAQrB;mBAAC,mBAAmB;mBACnB,mBAAmB,CAAC,EAAE,CAAC;qCAAA;IAK1B;QAAC,kBAAkB;QAClB,kBAAkB,CAAC,EAAE,CAAC;mBAMpB,mBAAmB;mBACnB,mBAAmB,CAAC,EAAE,CAAC;4CAPH;IAZvB;QAAC,kBAAkB;QAClB,kBAAkB,CAAC,EAAE,CAAC;6BAAA;IAxB3B;QAAC,eAAe;QACf,eAAe,CAAC,EAAE,CAAC;mBAGb,mBAAmB;mBACnB,mBAAmB,CAAC,EAAE,CAAC;mBAGvB,mBAAmB;mBACnB,mBAAmB,CAAC,EAAE,CAAC;eARV;IA6CpB,cAAC;AAAD,CAAC,AA5CD,IA4CC"} \ No newline at end of file +{"version":3,"file":"sourceMapValidationDecorators.js","sourceRoot":"","sources":["sourceMapValidationDecorators.ts"],"names":[],"mappings":";;;;;;;;;AASA;IACI,iBAGS,QAAgB;QAIvB,WAAc;aAAd,WAAc,CAAd,sBAAc,CAAd,IAAc;YAAd,0BAAc;;QAJP,aAAQ,GAAR,QAAQ,CAAQ;IAKzB,CAAC;IAID,uBAAK,GAAL;QACI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC5C,CAAC;IAUO,oBAAE,GAAV,UAGE,CAAS;QACP,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAID,sBAAI,8BAAS;aAAb;YACI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;QACzB,CAAC;aAED,UAGE,SAAiB;YACf,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC9B,CAAC;;;OAPA;IAQL,cAAC;AAAD,CAAC,AA5CD;AAuBmB,UAAE,GAAW,EAAE,CAAC;AAZ/B;IAAC,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,CAAC;oCAAA;AAKvB;IAAC,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,CAAC;kCAAA;AAQrB;eAAC,mBAAmB;eACnB,mBAAmB,CAAC,EAAE,CAAC;iCAAA;AAK1B;IAAC,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,CAAC;eAMpB,mBAAmB;eACnB,mBAAmB,CAAC,EAAE,CAAC;wCAPH;AAZvB;IAAC,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,CAAC;yBAAA;AAxB3B;IAAC,eAAe;IACf,eAAe,CAAC,EAAE,CAAC;eAGb,mBAAmB;eACnB,mBAAmB,CAAC,EAAE,CAAC;eAGvB,mBAAmB;eACnB,mBAAmB,CAAC,EAAE,CAAC;WARV;AA6CnB"} \ No newline at end of file diff --git a/tests/baselines/reference/sourceMapValidationDecorators.sourcemap.txt b/tests/baselines/reference/sourceMapValidationDecorators.sourcemap.txt index 168204288569d..58b5eaefc7ca5 100644 --- a/tests/baselines/reference/sourceMapValidationDecorators.sourcemap.txt +++ b/tests/baselines/reference/sourceMapValidationDecorators.sourcemap.txt @@ -356,113 +356,164 @@ sourceFile:sourceMapValidationDecorators.ts >>> configurable: true >>> }); 1->^^^^^^^ -2 > ^^^^^^^^^^^^^^-> +2 > ^^^^^^^^^^^^^-> 1-> 1->Emitted(33, 8) Source(46, 6) + SourceIndex(0) --- ->>> Greeter.x1 = 10; +>>> return Greeter; 1->^^^^ -2 > ^^^^^^^^^^ -3 > ^^^ -4 > ^^ -5 > ^ +2 > ^^^^^^^^^^^^^^ +1-> + > + > set greetings( + > @ParameterDecorator1 + > @ParameterDecorator2(90) + > greetings: string) { + > this.greeting = greetings; + > } + > +2 > } +1->Emitted(34, 5) Source(54, 1) + SourceIndex(0) +2 >Emitted(34, 19) Source(54, 2) + SourceIndex(0) +--- +>>>}()); +1 > +2 >^ +3 > +4 > ^^^^^^^^^^^^^^^^-> +1 > +2 >} +3 > +1 >Emitted(35, 1) Source(54, 1) + SourceIndex(0) +2 >Emitted(35, 2) Source(54, 2) + SourceIndex(0) +3 >Emitted(35, 2) Source(10, 1) + SourceIndex(0) +--- +>>>Greeter.x1 = 10; 1-> -2 > x1 -3 > : number = -4 > 10 -5 > ; -1->Emitted(34, 5) Source(33, 20) + SourceIndex(0) -2 >Emitted(34, 15) Source(33, 22) + SourceIndex(0) -3 >Emitted(34, 18) Source(33, 33) + SourceIndex(0) -4 >Emitted(34, 20) Source(33, 35) + SourceIndex(0) -5 >Emitted(34, 21) Source(33, 36) + SourceIndex(0) ---- ->>> __decorate([ -1 >^^^^ -2 > ^^^^^^^^^^^^^^^^^^^^^^^^-> +2 >^^^^^^^^^^ +3 > ^^^ +4 > ^^ +5 > ^ +1->class Greeter { + > constructor( + > @ParameterDecorator1 + > @ParameterDecorator2(20) + > public greeting: string, + > + > @ParameterDecorator1 + > @ParameterDecorator2(30) + > ...b: string[]) { + > } + > + > @PropertyDecorator1 + > @PropertyDecorator2(40) + > greet() { + > return "

" + this.greeting + "

"; + > } + > + > @PropertyDecorator1 + > @PropertyDecorator2(50) + > private x: string; + > + > @PropertyDecorator1 + > @PropertyDecorator2(60) + > private static +2 >x1 +3 > : number = +4 > 10 +5 > ; +1->Emitted(36, 1) Source(33, 20) + SourceIndex(0) +2 >Emitted(36, 11) Source(33, 22) + SourceIndex(0) +3 >Emitted(36, 14) Source(33, 33) + SourceIndex(0) +4 >Emitted(36, 16) Source(33, 35) + SourceIndex(0) +5 >Emitted(36, 17) Source(33, 36) + SourceIndex(0) +--- +>>>__decorate([ 1 > -1 >Emitted(35, 5) Source(21, 5) + SourceIndex(0) +2 >^^^^^^^^^^^^^^^^^^^^^^^^-> +1 > +1 >Emitted(37, 1) Source(21, 5) + SourceIndex(0) --- ->>> PropertyDecorator1, -1->^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^ -3 > ^^^^^-> +>>> PropertyDecorator1, +1->^^^^ +2 > ^^^^^^^^^^^^^^^^^^ +3 > ^^^^^-> 1->@ -2 > PropertyDecorator1 -1->Emitted(36, 9) Source(21, 6) + SourceIndex(0) -2 >Emitted(36, 27) Source(21, 24) + SourceIndex(0) +2 > PropertyDecorator1 +1->Emitted(38, 5) Source(21, 6) + SourceIndex(0) +2 >Emitted(38, 23) Source(21, 24) + SourceIndex(0) --- ->>> PropertyDecorator2(40) -1->^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^ -3 > ^ -4 > ^^ -5 > ^ -6 > ^^^^^^^^^^^^-> +>>> PropertyDecorator2(40) +1->^^^^ +2 > ^^^^^^^^^^^^^^^^^^ +3 > ^ +4 > ^^ +5 > ^ +6 > ^^^^^^^^^^^^-> 1-> > @ -2 > PropertyDecorator2 -3 > ( -4 > 40 -5 > ) -1->Emitted(37, 9) Source(22, 6) + SourceIndex(0) -2 >Emitted(37, 27) Source(22, 24) + SourceIndex(0) -3 >Emitted(37, 28) Source(22, 25) + SourceIndex(0) -4 >Emitted(37, 30) Source(22, 27) + SourceIndex(0) -5 >Emitted(37, 31) Source(22, 28) + SourceIndex(0) ---- ->>> ], Greeter.prototype, "greet", null); -1->^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2 > PropertyDecorator2 +3 > ( +4 > 40 +5 > ) +1->Emitted(39, 5) Source(22, 6) + SourceIndex(0) +2 >Emitted(39, 23) Source(22, 24) + SourceIndex(0) +3 >Emitted(39, 24) Source(22, 25) + SourceIndex(0) +4 >Emitted(39, 26) Source(22, 27) + SourceIndex(0) +5 >Emitted(39, 27) Source(22, 28) + SourceIndex(0) +--- +>>>], Greeter.prototype, "greet", null); +1->^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1-> -1->Emitted(38, 41) Source(22, 28) + SourceIndex(0) +1->Emitted(40, 37) Source(22, 28) + SourceIndex(0) --- ->>> __decorate([ -1 >^^^^ -2 > ^^^^^^^^^^^^^^^^^^^^^^^^-> +>>>__decorate([ +1 > +2 >^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > greet() { > return "

" + this.greeting + "

"; > } > > -1 >Emitted(39, 5) Source(27, 5) + SourceIndex(0) +1 >Emitted(41, 1) Source(27, 5) + SourceIndex(0) --- ->>> PropertyDecorator1, -1->^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^ -3 > ^^^^^-> +>>> PropertyDecorator1, +1->^^^^ +2 > ^^^^^^^^^^^^^^^^^^ +3 > ^^^^^-> 1->@ -2 > PropertyDecorator1 -1->Emitted(40, 9) Source(27, 6) + SourceIndex(0) -2 >Emitted(40, 27) Source(27, 24) + SourceIndex(0) +2 > PropertyDecorator1 +1->Emitted(42, 5) Source(27, 6) + SourceIndex(0) +2 >Emitted(42, 23) Source(27, 24) + SourceIndex(0) --- ->>> PropertyDecorator2(50) -1->^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^ -3 > ^ -4 > ^^ -5 > ^ -6 > ^^^^^^^^^^-> +>>> PropertyDecorator2(50) +1->^^^^ +2 > ^^^^^^^^^^^^^^^^^^ +3 > ^ +4 > ^^ +5 > ^ +6 > ^^^^^^^^^^-> 1-> > @ -2 > PropertyDecorator2 -3 > ( -4 > 50 -5 > ) -1->Emitted(41, 9) Source(28, 6) + SourceIndex(0) -2 >Emitted(41, 27) Source(28, 24) + SourceIndex(0) -3 >Emitted(41, 28) Source(28, 25) + SourceIndex(0) -4 >Emitted(41, 30) Source(28, 27) + SourceIndex(0) -5 >Emitted(41, 31) Source(28, 28) + SourceIndex(0) ---- ->>> ], Greeter.prototype, "x", void 0); -1->^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2 > PropertyDecorator2 +3 > ( +4 > 50 +5 > ) +1->Emitted(43, 5) Source(28, 6) + SourceIndex(0) +2 >Emitted(43, 23) Source(28, 24) + SourceIndex(0) +3 >Emitted(43, 24) Source(28, 25) + SourceIndex(0) +4 >Emitted(43, 26) Source(28, 27) + SourceIndex(0) +5 >Emitted(43, 27) Source(28, 28) + SourceIndex(0) +--- +>>>], Greeter.prototype, "x", void 0); +1->^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1-> -1->Emitted(42, 39) Source(28, 28) + SourceIndex(0) +1->Emitted(44, 35) Source(28, 28) + SourceIndex(0) --- ->>> __decorate([ -1 >^^^^ -2 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +>>>__decorate([ +1 > +2 >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > private x: string; > @@ -472,83 +523,83 @@ sourceFile:sourceMapValidationDecorators.ts > > private fn( > -1 >Emitted(43, 5) Source(36, 7) + SourceIndex(0) +1 >Emitted(45, 1) Source(36, 7) + SourceIndex(0) --- ->>> __param(0, ParameterDecorator1), -1->^^^^^^^^^^^^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^^ -3 > ^^^^^^-> +>>> __param(0, ParameterDecorator1), +1->^^^^^^^^^^^^^^^ +2 > ^^^^^^^^^^^^^^^^^^^ +3 > ^^^^^^-> 1->@ -2 > ParameterDecorator1 -1->Emitted(44, 20) Source(36, 8) + SourceIndex(0) -2 >Emitted(44, 39) Source(36, 27) + SourceIndex(0) ---- ->>> __param(0, ParameterDecorator2(70)) -1->^^^^^^^^^^^^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^^ -3 > ^ -4 > ^^ -5 > ^ +2 > ParameterDecorator1 +1->Emitted(46, 16) Source(36, 8) + SourceIndex(0) +2 >Emitted(46, 35) Source(36, 27) + SourceIndex(0) +--- +>>> __param(0, ParameterDecorator2(70)) +1->^^^^^^^^^^^^^^^ +2 > ^^^^^^^^^^^^^^^^^^^ +3 > ^ +4 > ^^ +5 > ^ 1-> > @ -2 > ParameterDecorator2 -3 > ( -4 > 70 -5 > ) -1->Emitted(45, 20) Source(37, 8) + SourceIndex(0) -2 >Emitted(45, 39) Source(37, 27) + SourceIndex(0) -3 >Emitted(45, 40) Source(37, 28) + SourceIndex(0) -4 >Emitted(45, 42) Source(37, 30) + SourceIndex(0) -5 >Emitted(45, 43) Source(37, 31) + SourceIndex(0) ---- ->>> ], Greeter.prototype, "fn", null); -1 >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2 > ParameterDecorator2 +3 > ( +4 > 70 +5 > ) +1->Emitted(47, 16) Source(37, 8) + SourceIndex(0) +2 >Emitted(47, 35) Source(37, 27) + SourceIndex(0) +3 >Emitted(47, 36) Source(37, 28) + SourceIndex(0) +4 >Emitted(47, 38) Source(37, 30) + SourceIndex(0) +5 >Emitted(47, 39) Source(37, 31) + SourceIndex(0) +--- +>>>], Greeter.prototype, "fn", null); +1 >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1 > -1 >Emitted(46, 38) Source(37, 31) + SourceIndex(0) +1 >Emitted(48, 34) Source(37, 31) + SourceIndex(0) --- ->>> __decorate([ -1 >^^^^ -2 > ^^^^^^^^^^^^^^^^^^^^^^^^-> +>>>__decorate([ +1 > +2 >^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > x: number) { > return this.greeting; > } > > -1 >Emitted(47, 5) Source(42, 5) + SourceIndex(0) +1 >Emitted(49, 1) Source(42, 5) + SourceIndex(0) --- ->>> PropertyDecorator1, -1->^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^ -3 > ^^^^^^-> +>>> PropertyDecorator1, +1->^^^^ +2 > ^^^^^^^^^^^^^^^^^^ +3 > ^^^^^^-> 1->@ -2 > PropertyDecorator1 -1->Emitted(48, 9) Source(42, 6) + SourceIndex(0) -2 >Emitted(48, 27) Source(42, 24) + SourceIndex(0) +2 > PropertyDecorator1 +1->Emitted(50, 5) Source(42, 6) + SourceIndex(0) +2 >Emitted(50, 23) Source(42, 24) + SourceIndex(0) --- ->>> PropertyDecorator2(80), -1->^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^ -3 > ^ -4 > ^^ -5 > ^ -6 > ^^^^^^^^^^^-> +>>> PropertyDecorator2(80), +1->^^^^ +2 > ^^^^^^^^^^^^^^^^^^ +3 > ^ +4 > ^^ +5 > ^ +6 > ^^^^^^^^^^^-> 1-> > @ -2 > PropertyDecorator2 -3 > ( -4 > 80 -5 > ) -1->Emitted(49, 9) Source(43, 6) + SourceIndex(0) -2 >Emitted(49, 27) Source(43, 24) + SourceIndex(0) -3 >Emitted(49, 28) Source(43, 25) + SourceIndex(0) -4 >Emitted(49, 30) Source(43, 27) + SourceIndex(0) -5 >Emitted(49, 31) Source(43, 28) + SourceIndex(0) ---- ->>> __param(0, ParameterDecorator1), -1->^^^^^^^^^^^^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^^ -3 > ^^^^^^-> +2 > PropertyDecorator2 +3 > ( +4 > 80 +5 > ) +1->Emitted(51, 5) Source(43, 6) + SourceIndex(0) +2 >Emitted(51, 23) Source(43, 24) + SourceIndex(0) +3 >Emitted(51, 24) Source(43, 25) + SourceIndex(0) +4 >Emitted(51, 26) Source(43, 27) + SourceIndex(0) +5 >Emitted(51, 27) Source(43, 28) + SourceIndex(0) +--- +>>> __param(0, ParameterDecorator1), +1->^^^^^^^^^^^^^^^ +2 > ^^^^^^^^^^^^^^^^^^^ +3 > ^^^^^^-> 1-> > get greetings() { > return this.greeting; @@ -556,176 +607,175 @@ sourceFile:sourceMapValidationDecorators.ts > > set greetings( > @ -2 > ParameterDecorator1 -1->Emitted(50, 20) Source(49, 8) + SourceIndex(0) -2 >Emitted(50, 39) Source(49, 27) + SourceIndex(0) ---- ->>> __param(0, ParameterDecorator2(90)) -1->^^^^^^^^^^^^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^^ -3 > ^ -4 > ^^ -5 > ^ -6 > ^^^^-> +2 > ParameterDecorator1 +1->Emitted(52, 16) Source(49, 8) + SourceIndex(0) +2 >Emitted(52, 35) Source(49, 27) + SourceIndex(0) +--- +>>> __param(0, ParameterDecorator2(90)) +1->^^^^^^^^^^^^^^^ +2 > ^^^^^^^^^^^^^^^^^^^ +3 > ^ +4 > ^^ +5 > ^ +6 > ^^^^-> 1-> > @ -2 > ParameterDecorator2 -3 > ( -4 > 90 -5 > ) -1->Emitted(51, 20) Source(50, 8) + SourceIndex(0) -2 >Emitted(51, 39) Source(50, 27) + SourceIndex(0) -3 >Emitted(51, 40) Source(50, 28) + SourceIndex(0) -4 >Emitted(51, 42) Source(50, 30) + SourceIndex(0) -5 >Emitted(51, 43) Source(50, 31) + SourceIndex(0) ---- ->>> ], Greeter.prototype, "greetings", null); -1->^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2 > ParameterDecorator2 +3 > ( +4 > 90 +5 > ) +1->Emitted(53, 16) Source(50, 8) + SourceIndex(0) +2 >Emitted(53, 35) Source(50, 27) + SourceIndex(0) +3 >Emitted(53, 36) Source(50, 28) + SourceIndex(0) +4 >Emitted(53, 38) Source(50, 30) + SourceIndex(0) +5 >Emitted(53, 39) Source(50, 31) + SourceIndex(0) +--- +>>>], Greeter.prototype, "greetings", null); +1->^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1-> -1->Emitted(52, 45) Source(43, 28) + SourceIndex(0) +1->Emitted(54, 41) Source(43, 28) + SourceIndex(0) --- ->>> __decorate([ -1 >^^^^ -2 > ^^^^^^^^^^^^^^^^^^^^^^^^-> +>>>__decorate([ +1 > +2 >^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > -1 >Emitted(53, 5) Source(31, 5) + SourceIndex(0) +1 >Emitted(55, 1) Source(31, 5) + SourceIndex(0) --- ->>> PropertyDecorator1, -1->^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^ -3 > ^^^^^-> +>>> PropertyDecorator1, +1->^^^^ +2 > ^^^^^^^^^^^^^^^^^^ +3 > ^^^^^-> 1->@ -2 > PropertyDecorator1 -1->Emitted(54, 9) Source(31, 6) + SourceIndex(0) -2 >Emitted(54, 27) Source(31, 24) + SourceIndex(0) +2 > PropertyDecorator1 +1->Emitted(56, 5) Source(31, 6) + SourceIndex(0) +2 >Emitted(56, 23) Source(31, 24) + SourceIndex(0) --- ->>> PropertyDecorator2(60) -1->^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^ -3 > ^ -4 > ^^ -5 > ^ -6 > ^-> +>>> PropertyDecorator2(60) +1->^^^^ +2 > ^^^^^^^^^^^^^^^^^^ +3 > ^ +4 > ^^ +5 > ^ +6 > ^-> 1-> > @ -2 > PropertyDecorator2 -3 > ( -4 > 60 -5 > ) -1->Emitted(55, 9) Source(32, 6) + SourceIndex(0) -2 >Emitted(55, 27) Source(32, 24) + SourceIndex(0) -3 >Emitted(55, 28) Source(32, 25) + SourceIndex(0) -4 >Emitted(55, 30) Source(32, 27) + SourceIndex(0) -5 >Emitted(55, 31) Source(32, 28) + SourceIndex(0) ---- ->>> ], Greeter, "x1", void 0); -1->^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2 > PropertyDecorator2 +3 > ( +4 > 60 +5 > ) +1->Emitted(57, 5) Source(32, 6) + SourceIndex(0) +2 >Emitted(57, 23) Source(32, 24) + SourceIndex(0) +3 >Emitted(57, 24) Source(32, 25) + SourceIndex(0) +4 >Emitted(57, 26) Source(32, 27) + SourceIndex(0) +5 >Emitted(57, 27) Source(32, 28) + SourceIndex(0) +--- +>>>], Greeter, "x1", void 0); +1->^^^^^^^^^^^^^^^^^^^^^^^^^ 1-> -1->Emitted(56, 30) Source(32, 28) + SourceIndex(0) +1->Emitted(58, 26) Source(32, 28) + SourceIndex(0) --- ->>> Greeter = __decorate([ -1 >^^^^ -2 > ^^^^^^^^^^^^^^^^^^^^^-> +>>>Greeter = __decorate([ +1 > +2 >^^^^^^^^^^^^^^^^^^^^^-> 1 > -1 >Emitted(57, 5) Source(8, 1) + SourceIndex(0) +1 >Emitted(59, 1) Source(8, 1) + SourceIndex(0) --- ->>> ClassDecorator1, -1->^^^^^^^^ -2 > ^^^^^^^^^^^^^^^ -3 > ^^^^^^-> +>>> ClassDecorator1, +1->^^^^ +2 > ^^^^^^^^^^^^^^^ +3 > ^^^^^^-> 1->@ -2 > ClassDecorator1 -1->Emitted(58, 9) Source(8, 2) + SourceIndex(0) -2 >Emitted(58, 24) Source(8, 17) + SourceIndex(0) +2 > ClassDecorator1 +1->Emitted(60, 5) Source(8, 2) + SourceIndex(0) +2 >Emitted(60, 20) Source(8, 17) + SourceIndex(0) --- ->>> ClassDecorator2(10), -1->^^^^^^^^ -2 > ^^^^^^^^^^^^^^^ -3 > ^ -4 > ^^ -5 > ^ -6 > ^^^^^^^^^^^^^^-> +>>> ClassDecorator2(10), +1->^^^^ +2 > ^^^^^^^^^^^^^^^ +3 > ^ +4 > ^^ +5 > ^ +6 > ^^^^^^^^^^^^^^-> 1-> >@ -2 > ClassDecorator2 -3 > ( -4 > 10 -5 > ) -1->Emitted(59, 9) Source(9, 2) + SourceIndex(0) -2 >Emitted(59, 24) Source(9, 17) + SourceIndex(0) -3 >Emitted(59, 25) Source(9, 18) + SourceIndex(0) -4 >Emitted(59, 27) Source(9, 20) + SourceIndex(0) -5 >Emitted(59, 28) Source(9, 21) + SourceIndex(0) ---- ->>> __param(0, ParameterDecorator1), -1->^^^^^^^^^^^^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^^ -3 > ^^^^^^^-> +2 > ClassDecorator2 +3 > ( +4 > 10 +5 > ) +1->Emitted(61, 5) Source(9, 2) + SourceIndex(0) +2 >Emitted(61, 20) Source(9, 17) + SourceIndex(0) +3 >Emitted(61, 21) Source(9, 18) + SourceIndex(0) +4 >Emitted(61, 23) Source(9, 20) + SourceIndex(0) +5 >Emitted(61, 24) Source(9, 21) + SourceIndex(0) +--- +>>> __param(0, ParameterDecorator1), +1->^^^^^^^^^^^^^^^ +2 > ^^^^^^^^^^^^^^^^^^^ +3 > ^^^^^^^-> 1-> >class Greeter { > constructor( > @ -2 > ParameterDecorator1 -1->Emitted(60, 20) Source(12, 8) + SourceIndex(0) -2 >Emitted(60, 39) Source(12, 27) + SourceIndex(0) ---- ->>> __param(0, ParameterDecorator2(20)), -1->^^^^^^^^^^^^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^^ -3 > ^ -4 > ^^ -5 > ^ +2 > ParameterDecorator1 +1->Emitted(62, 16) Source(12, 8) + SourceIndex(0) +2 >Emitted(62, 35) Source(12, 27) + SourceIndex(0) +--- +>>> __param(0, ParameterDecorator2(20)), +1->^^^^^^^^^^^^^^^ +2 > ^^^^^^^^^^^^^^^^^^^ +3 > ^ +4 > ^^ +5 > ^ 1-> > @ -2 > ParameterDecorator2 -3 > ( -4 > 20 -5 > ) -1->Emitted(61, 20) Source(13, 8) + SourceIndex(0) -2 >Emitted(61, 39) Source(13, 27) + SourceIndex(0) -3 >Emitted(61, 40) Source(13, 28) + SourceIndex(0) -4 >Emitted(61, 42) Source(13, 30) + SourceIndex(0) -5 >Emitted(61, 43) Source(13, 31) + SourceIndex(0) ---- ->>> __param(1, ParameterDecorator1), -1 >^^^^^^^^^^^^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^^ -3 > ^^^^^^-> +2 > ParameterDecorator2 +3 > ( +4 > 20 +5 > ) +1->Emitted(63, 16) Source(13, 8) + SourceIndex(0) +2 >Emitted(63, 35) Source(13, 27) + SourceIndex(0) +3 >Emitted(63, 36) Source(13, 28) + SourceIndex(0) +4 >Emitted(63, 38) Source(13, 30) + SourceIndex(0) +5 >Emitted(63, 39) Source(13, 31) + SourceIndex(0) +--- +>>> __param(1, ParameterDecorator1), +1 >^^^^^^^^^^^^^^^ +2 > ^^^^^^^^^^^^^^^^^^^ +3 > ^^^^^^-> 1 > > public greeting: string, > > @ -2 > ParameterDecorator1 -1 >Emitted(62, 20) Source(16, 8) + SourceIndex(0) -2 >Emitted(62, 39) Source(16, 27) + SourceIndex(0) ---- ->>> __param(1, ParameterDecorator2(30)) -1->^^^^^^^^^^^^^^^^^^^ -2 > ^^^^^^^^^^^^^^^^^^^ -3 > ^ -4 > ^^ -5 > ^ +2 > ParameterDecorator1 +1 >Emitted(64, 16) Source(16, 8) + SourceIndex(0) +2 >Emitted(64, 35) Source(16, 27) + SourceIndex(0) +--- +>>> __param(1, ParameterDecorator2(30)) +1->^^^^^^^^^^^^^^^ +2 > ^^^^^^^^^^^^^^^^^^^ +3 > ^ +4 > ^^ +5 > ^ 1-> > @ -2 > ParameterDecorator2 -3 > ( -4 > 30 -5 > ) -1->Emitted(63, 20) Source(17, 8) + SourceIndex(0) -2 >Emitted(63, 39) Source(17, 27) + SourceIndex(0) -3 >Emitted(63, 40) Source(17, 28) + SourceIndex(0) -4 >Emitted(63, 42) Source(17, 30) + SourceIndex(0) -5 >Emitted(63, 43) Source(17, 31) + SourceIndex(0) ---- ->>> ], Greeter); -1 >^^^^^^^^^^^^^^^ -2 > ^^^^^-> +2 > ParameterDecorator2 +3 > ( +4 > 30 +5 > ) +1->Emitted(65, 16) Source(17, 8) + SourceIndex(0) +2 >Emitted(65, 35) Source(17, 27) + SourceIndex(0) +3 >Emitted(65, 36) Source(17, 28) + SourceIndex(0) +4 >Emitted(65, 38) Source(17, 30) + SourceIndex(0) +5 >Emitted(65, 39) Source(17, 31) + SourceIndex(0) +--- +>>>], Greeter); +1 >^^^^^^^^^^^ +2 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > -1 >Emitted(64, 16) Source(9, 21) + SourceIndex(0) +1 >Emitted(66, 12) Source(9, 21) + SourceIndex(0) --- ->>> return Greeter; -1->^^^^ -2 > ^^^^^^^^^^^^^^ +>>>//# sourceMappingURL=sourceMapValidationDecorators.js.map1-> +2 >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1-> >class Greeter { > constructor( @@ -771,68 +821,6 @@ sourceFile:sourceMapValidationDecorators.ts > greetings: string) { > this.greeting = greetings; > } - > -2 > } -1->Emitted(65, 5) Source(54, 1) + SourceIndex(0) -2 >Emitted(65, 19) Source(54, 2) + SourceIndex(0) ---- ->>>}()); -1 > -2 >^ -3 > -4 > ^^^^ -5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> -1 > -2 >} -3 > -4 > class Greeter { - > constructor( - > @ParameterDecorator1 - > @ParameterDecorator2(20) - > public greeting: string, - > - > @ParameterDecorator1 - > @ParameterDecorator2(30) - > ...b: string[]) { - > } - > - > @PropertyDecorator1 - > @PropertyDecorator2(40) - > greet() { - > return "

" + this.greeting + "

"; - > } - > - > @PropertyDecorator1 - > @PropertyDecorator2(50) - > private x: string; - > - > @PropertyDecorator1 - > @PropertyDecorator2(60) - > private static x1: number = 10; - > - > private fn( - > @ParameterDecorator1 - > @ParameterDecorator2(70) - > x: number) { - > return this.greeting; - > } - > - > @PropertyDecorator1 - > @PropertyDecorator2(80) - > get greetings() { - > return this.greeting; - > } - > - > set greetings( - > @ParameterDecorator1 - > @ParameterDecorator2(90) - > greetings: string) { - > this.greeting = greetings; - > } - > } -1 >Emitted(66, 1) Source(54, 1) + SourceIndex(0) -2 >Emitted(66, 2) Source(54, 2) + SourceIndex(0) -3 >Emitted(66, 2) Source(10, 1) + SourceIndex(0) -4 >Emitted(66, 6) Source(54, 2) + SourceIndex(0) ---- ->>>//# sourceMappingURL=sourceMapValidationDecorators.js.map \ No newline at end of file + >} +1->Emitted(67, 1) Source(54, 2) + SourceIndex(0) +--- \ No newline at end of file diff --git a/tests/baselines/reference/staticClassProps.js b/tests/baselines/reference/staticClassProps.js index c808e4eb7fd81..8a61bb7400fe7 100644 --- a/tests/baselines/reference/staticClassProps.js +++ b/tests/baselines/reference/staticClassProps.js @@ -13,6 +13,6 @@ var C = (function () { function C() { } C.prototype.foo = function () { }; - C.z = 1; return C; }()); +C.z = 1; diff --git a/tests/baselines/reference/staticMemberAccessOffDerivedType1.js b/tests/baselines/reference/staticMemberAccessOffDerivedType1.js index a05ece1dbbbcc..1c76a2512208f 100644 --- a/tests/baselines/reference/staticMemberAccessOffDerivedType1.js +++ b/tests/baselines/reference/staticMemberAccessOffDerivedType1.js @@ -28,6 +28,6 @@ var P = (function (_super) { function P() { _super.apply(this, arguments); } - P.SomeNumber = P.GetNumber(); return P; }(SomeBase)); +P.SomeNumber = P.GetNumber(); diff --git a/tests/baselines/reference/staticMemberInitialization.js b/tests/baselines/reference/staticMemberInitialization.js index c21c29ed4f815..13a5c8c25c6aa 100644 --- a/tests/baselines/reference/staticMemberInitialization.js +++ b/tests/baselines/reference/staticMemberInitialization.js @@ -10,8 +10,8 @@ var r = C.x; var C = (function () { function C() { } - C.x = 1; return C; }()); +C.x = 1; var c = new C(); var r = C.x; diff --git a/tests/baselines/reference/staticMemberWithStringAndNumberNames.js b/tests/baselines/reference/staticMemberWithStringAndNumberNames.js index 2fdd64ea04b76..9d8846ba963f0 100644 --- a/tests/baselines/reference/staticMemberWithStringAndNumberNames.js +++ b/tests/baselines/reference/staticMemberWithStringAndNumberNames.js @@ -19,10 +19,10 @@ var C = (function () { this.x2 = C['0']; this.x3 = C[0]; } - C["foo"] = 0; - C[0] = 1; - C.s = C['foo']; - C.s2 = C['0']; - C.s3 = C[0]; return C; }()); +C["foo"] = 0; +C[0] = 1; +C.s = C['foo']; +C.s2 = C['0']; +C.s3 = C[0]; diff --git a/tests/baselines/reference/staticModifierAlreadySeen.js b/tests/baselines/reference/staticModifierAlreadySeen.js index 76f8b050d6f39..c37d4c0807ad3 100644 --- a/tests/baselines/reference/staticModifierAlreadySeen.js +++ b/tests/baselines/reference/staticModifierAlreadySeen.js @@ -9,6 +9,6 @@ var C = (function () { function C() { } C.bar = function () { }; - C.foo = 1; return C; }()); +C.foo = 1; diff --git a/tests/baselines/reference/staticPropSuper.js b/tests/baselines/reference/staticPropSuper.js index f4bc5ece8d1cd..c25c406746dfb 100644 --- a/tests/baselines/reference/staticPropSuper.js +++ b/tests/baselines/reference/staticPropSuper.js @@ -52,9 +52,9 @@ var B = (function (_super) { var x = 1; // should not error _super.call(this); } - B.s = 9; return B; }(A)); +B.s = 9; var C = (function (_super) { __extends(C, _super); function C() { diff --git a/tests/baselines/reference/statics.js b/tests/baselines/reference/statics.js index 3776c09c8ceae..d52a28208e6c9 100644 --- a/tests/baselines/reference/statics.js +++ b/tests/baselines/reference/statics.js @@ -45,11 +45,11 @@ var M; C.f = function (n) { return "wow: " + (n + C.y + C.pub + C.priv); }; - C.priv = 2; - C.pub = 3; - C.y = C.priv; return C; }()); + C.priv = 2; + C.pub = 3; + C.y = C.priv; M.C = C; var c = C.y; function f() { diff --git a/tests/baselines/reference/staticsInConstructorBodies.js b/tests/baselines/reference/staticsInConstructorBodies.js index 9bc3ed293251c..edd49456ef711 100644 --- a/tests/baselines/reference/staticsInConstructorBodies.js +++ b/tests/baselines/reference/staticsInConstructorBodies.js @@ -11,6 +11,6 @@ var C = (function () { function C() { } C.m1 = function () { }; // ERROR - C.p1 = 0; // ERROR return C; }()); +C.p1 = 0; // ERROR diff --git a/tests/baselines/reference/staticsNotInScopeInClodule.js b/tests/baselines/reference/staticsNotInScopeInClodule.js index 150ae8aca5b24..e10ed2b31d387 100644 --- a/tests/baselines/reference/staticsNotInScopeInClodule.js +++ b/tests/baselines/reference/staticsNotInScopeInClodule.js @@ -11,9 +11,9 @@ module Clod { var Clod = (function () { function Clod() { } - Clod.x = 10; return Clod; }()); +Clod.x = 10; var Clod; (function (Clod) { var p = x; // x isn't in scope here diff --git a/tests/baselines/reference/strictModeInConstructor.js b/tests/baselines/reference/strictModeInConstructor.js index 3b31586154856..30dbfa58d7026 100644 --- a/tests/baselines/reference/strictModeInConstructor.js +++ b/tests/baselines/reference/strictModeInConstructor.js @@ -105,18 +105,18 @@ var Bs = (function (_super) { "use strict"; // No error _super.call(this); } - Bs.s = 9; return Bs; }(A)); +Bs.s = 9; var Cs = (function (_super) { __extends(Cs, _super); function Cs() { _super.call(this); // No error "use strict"; } - Cs.s = 9; return Cs; }(A)); +Cs.s = 9; var Ds = (function (_super) { __extends(Ds, _super); function Ds() { @@ -124,6 +124,6 @@ var Ds = (function (_super) { _super.call(this); "use strict"; } - Ds.s = 9; return Ds; }(A)); +Ds.s = 9; diff --git a/tests/baselines/reference/superAccess.js b/tests/baselines/reference/superAccess.js index 37c6a9816a605..6e5cd9aca4476 100644 --- a/tests/baselines/reference/superAccess.js +++ b/tests/baselines/reference/superAccess.js @@ -24,9 +24,9 @@ var MyBase = (function () { this.S2 = "test"; this.f = function () { return 5; }; } - MyBase.S1 = 5; return MyBase; }()); +MyBase.S1 = 5; var MyDerived = (function (_super) { __extends(MyDerived, _super); function MyDerived() { diff --git a/tests/baselines/reference/superAccess2.js b/tests/baselines/reference/superAccess2.js index b27d695cb31bc..c72bf387c24b4 100644 --- a/tests/baselines/reference/superAccess2.js +++ b/tests/baselines/reference/superAccess2.js @@ -59,6 +59,6 @@ var Q = (function (_super) { _super.x.call(this); // error _super.y.call(this); }; - Q.yy = _super.; // error for static initializer accessing super return Q; }(P)); +Q.yy = _super.; // error for static initializer accessing super diff --git a/tests/baselines/reference/thisInArrowFunctionInStaticInitializer1.js b/tests/baselines/reference/thisInArrowFunctionInStaticInitializer1.js index 4b23326c7a3d8..07d65c726b354 100644 --- a/tests/baselines/reference/thisInArrowFunctionInStaticInitializer1.js +++ b/tests/baselines/reference/thisInArrowFunctionInStaticInitializer1.js @@ -14,9 +14,9 @@ var Vector = (function () { function Vector() { var _this = this; } - Vector.foo = function () { - // 'this' should not be available in a static initializer. - log(_this); - }; return Vector; }()); +Vector.foo = function () { + // 'this' should not be available in a static initializer. + log(_this); +}; diff --git a/tests/baselines/reference/thisInConstructorParameter2.js b/tests/baselines/reference/thisInConstructorParameter2.js index 7260ecafcb98c..ba2917c5a4e66 100644 --- a/tests/baselines/reference/thisInConstructorParameter2.js +++ b/tests/baselines/reference/thisInConstructorParameter2.js @@ -25,6 +25,6 @@ var P = (function () { if (zz === void 0) { zz = this; } zz.y; }; - P.y = this; return P; }()); +P.y = this; diff --git a/tests/baselines/reference/thisInInvalidContexts.js b/tests/baselines/reference/thisInInvalidContexts.js index 75ccd12c2889c..76eeef0139c76 100644 --- a/tests/baselines/reference/thisInInvalidContexts.js +++ b/tests/baselines/reference/thisInInvalidContexts.js @@ -58,9 +58,9 @@ var __extends = (this && this.__extends) || function (d, b) { var ErrClass1 = (function () { function ErrClass1() { } - ErrClass1.t = this; // Error return ErrClass1; }()); +ErrClass1.t = this; // Error var BaseErrClass = (function () { function BaseErrClass(t) { } diff --git a/tests/baselines/reference/thisInInvalidContextsExternalModule.js b/tests/baselines/reference/thisInInvalidContextsExternalModule.js index 2e7e6d8b47cca..634b5ef0a2024 100644 --- a/tests/baselines/reference/thisInInvalidContextsExternalModule.js +++ b/tests/baselines/reference/thisInInvalidContextsExternalModule.js @@ -59,9 +59,9 @@ var __extends = (this && this.__extends) || function (d, b) { var ErrClass1 = (function () { function ErrClass1() { } - ErrClass1.t = this; // Error return ErrClass1; }()); +ErrClass1.t = this; // Error var BaseErrClass = (function () { function BaseErrClass(t) { } diff --git a/tests/baselines/reference/thisInOuterClassBody.js b/tests/baselines/reference/thisInOuterClassBody.js index 40e4a2e2485da..40b0e2b78ae58 100644 --- a/tests/baselines/reference/thisInOuterClassBody.js +++ b/tests/baselines/reference/thisInOuterClassBody.js @@ -36,6 +36,6 @@ var Foo = (function () { var a = this.y; var b = this.x; }; - Foo.y = this; return Foo; }()); +Foo.y = this; diff --git a/tests/baselines/reference/thisInPropertyBoundDeclarations.js b/tests/baselines/reference/thisInPropertyBoundDeclarations.js index 0b378cec51aaf..f5967fbb5b2ad 100644 --- a/tests/baselines/reference/thisInPropertyBoundDeclarations.js +++ b/tests/baselines/reference/thisInPropertyBoundDeclarations.js @@ -74,13 +74,13 @@ var Bug = (function () { Bug.prototype.foo = function (name) { this.name = name; }; - Bug.func = [ - function (that, name) { - that.foo(name); - } - ]; return Bug; }()); +Bug.func = [ + function (that, name) { + that.foo(name); + } +]; // Valid use of this in a property bound decl var A = (function () { function A() { diff --git a/tests/baselines/reference/thisInStaticMethod1.js b/tests/baselines/reference/thisInStaticMethod1.js index a42415c0b62b1..1e39ded3b6171 100644 --- a/tests/baselines/reference/thisInStaticMethod1.js +++ b/tests/baselines/reference/thisInStaticMethod1.js @@ -14,7 +14,7 @@ var foo = (function () { foo.bar = function () { return this.x; }; - foo.x = 3; return foo; }()); +foo.x = 3; var x = foo.bar(); diff --git a/tests/baselines/reference/thisTypeErrors.js b/tests/baselines/reference/thisTypeErrors.js index f246b80f4f4f6..5811ec6b70922 100644 --- a/tests/baselines/reference/thisTypeErrors.js +++ b/tests/baselines/reference/thisTypeErrors.js @@ -75,9 +75,9 @@ var C2 = (function () { C2.foo = function (x) { return undefined; }; - C2.y = undefined; return C2; }()); +C2.y = undefined; var N1; (function (N1) { N1.y = this; diff --git a/tests/baselines/reference/typeArgumentInferenceWithClassExpression1.js b/tests/baselines/reference/typeArgumentInferenceWithClassExpression1.js index d18181dd1dcd4..44b37cc3be568 100644 --- a/tests/baselines/reference/typeArgumentInferenceWithClassExpression1.js +++ b/tests/baselines/reference/typeArgumentInferenceWithClassExpression1.js @@ -14,9 +14,11 @@ function foo(x) { }()); } return undefined; } -foo((function () { - function class_2() { - } - class_2.prop = "hello"; - return class_2; -}())).length; +foo((_a = (function () { + function class_2() { + } + return class_2; + }()), + _a.prop = "hello", + _a)).length; +var _a; diff --git a/tests/baselines/reference/typeArgumentInferenceWithClassExpression2.js b/tests/baselines/reference/typeArgumentInferenceWithClassExpression2.js index 66da2ffbff602..bd221a3013733 100644 --- a/tests/baselines/reference/typeArgumentInferenceWithClassExpression2.js +++ b/tests/baselines/reference/typeArgumentInferenceWithClassExpression2.js @@ -16,9 +16,11 @@ function foo(x) { return undefined; } // Should not infer string because it is a static property -foo((function () { - function class_2() { - } - class_2.prop = "hello"; - return class_2; -}())).length; +foo((_a = (function () { + function class_2() { + } + return class_2; + }()), + _a.prop = "hello", + _a)).length; +var _a; diff --git a/tests/baselines/reference/typeOfPrototype.js b/tests/baselines/reference/typeOfPrototype.js index 3e8b31869361c..a0f076113e0c0 100644 --- a/tests/baselines/reference/typeOfPrototype.js +++ b/tests/baselines/reference/typeOfPrototype.js @@ -11,7 +11,7 @@ var Foo = (function () { function Foo() { this.bar = 3; } - Foo.bar = ''; return Foo; }()); +Foo.bar = ''; Foo.prototype.bar = undefined; // Should be OK diff --git a/tests/baselines/reference/typeOfThisInStaticMembers2.js b/tests/baselines/reference/typeOfThisInStaticMembers2.js index b01770446beea..886ea04e23db4 100644 --- a/tests/baselines/reference/typeOfThisInStaticMembers2.js +++ b/tests/baselines/reference/typeOfThisInStaticMembers2.js @@ -11,12 +11,12 @@ class C2 { var C = (function () { function C() { } - C.foo = this; // error return C; }()); +C.foo = this; // error var C2 = (function () { function C2() { } - C2.foo = this; // error return C2; }()); +C2.foo = this; // error diff --git a/tests/baselines/reference/typeQueryOnClass.js b/tests/baselines/reference/typeQueryOnClass.js index 85dff0de831e0..80623af5be827 100644 --- a/tests/baselines/reference/typeQueryOnClass.js +++ b/tests/baselines/reference/typeQueryOnClass.js @@ -99,10 +99,10 @@ var C = (function () { enumerable: true, configurable: true }); - C.sa = 1; - C.sb = function () { return 1; }; return C; }()); +C.sa = 1; +C.sb = function () { return 1; }; var c; // BUG 820454 var r1; diff --git a/tests/baselines/reference/unqualifiedCallToClassStatic1.js b/tests/baselines/reference/unqualifiedCallToClassStatic1.js index 6c78a737fe862..906e9d3795ff1 100644 --- a/tests/baselines/reference/unqualifiedCallToClassStatic1.js +++ b/tests/baselines/reference/unqualifiedCallToClassStatic1.js @@ -10,9 +10,9 @@ class Vector { var Vector = (function () { function Vector() { } - Vector.foo = function () { - // 'foo' cannot be called in an unqualified manner. - foo(); - }; return Vector; }()); +Vector.foo = function () { + // 'foo' cannot be called in an unqualified manner. + foo(); +}; diff --git a/tests/baselines/reference/witness.js b/tests/baselines/reference/witness.js index e0a4c35687325..56294a7cbb99b 100644 --- a/tests/baselines/reference/witness.js +++ b/tests/baselines/reference/witness.js @@ -262,9 +262,9 @@ var c2inst; var C3 = (function () { function C3() { } - C3.q = C3.q; return C3; }()); +C3.q = C3.q; var qq = C3.q; var qq; // Parentheses - tested a bunch above diff --git a/tests/cases/unittests/transpile.ts b/tests/cases/unittests/transpile.ts index 6f46bb53405c5..084b2b08eab08 100644 --- a/tests/cases/unittests/transpile.ts +++ b/tests/cases/unittests/transpile.ts @@ -2,26 +2,26 @@ module ts { describe("Transpile", () => { - + interface TranspileTestSettings { options?: TranspileOptions; expectedOutput?: string; expectedDiagnosticCodes?: number[]; } - + function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes?: number[]) { if(!expectedDiagnosticCodes) { return; } - + for (let i = 0; i < expectedDiagnosticCodes.length; i++) { assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expeced diagnostic.`); } - assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected"); + assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected"); } - + function test(input: string, testSettings: TranspileTestSettings): void { - + let transpileOptions: TranspileOptions = testSettings.options || {}; if (!transpileOptions.compilerOptions) { transpileOptions.compilerOptions = {}; @@ -30,43 +30,43 @@ module ts { // use \r\n as default new line transpileOptions.compilerOptions.newLine = ts.NewLineKind.CarriageReturnLineFeed; } - - let canUseOldTranspile = !transpileOptions.renamedDependencies; - + + let canUseOldTranspile = !transpileOptions.renamedDependencies; + transpileOptions.reportDiagnostics = true; let transpileModuleResult = transpileModule(input, transpileOptions); - + checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes); - + if (testSettings.expectedOutput !== undefined) { assert.equal(transpileModuleResult.outputText, testSettings.expectedOutput); } - + if (canUseOldTranspile) { let diagnostics: Diagnostic[] = []; - let transpileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, diagnostics, transpileOptions.moduleName); + let transpileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, diagnostics, transpileOptions.moduleName); checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes); if (testSettings.expectedOutput) { assert.equal(transpileResult, testSettings.expectedOutput); } } - + // check source maps if (!transpileOptions.compilerOptions) { transpileOptions.compilerOptions = {}; } - + if (!transpileOptions.fileName) { transpileOptions.fileName = transpileOptions.compilerOptions.jsx ? "file.tsx" : "file.ts"; } - + transpileOptions.compilerOptions.sourceMap = true; let transpileModuleResultWithSourceMap = transpileModule(input, transpileOptions); assert.isTrue(transpileModuleResultWithSourceMap.sourceMapText !== undefined); - + let expectedSourceMapFileName = removeFileExtension(getBaseFileName(normalizeSlashes(transpileOptions.fileName))) + ".js.map"; let expectedSourceMappingUrlLine = `//# sourceMappingURL=${expectedSourceMapFileName}`; - + if (testSettings.expectedOutput !== undefined) { assert.equal(transpileModuleResultWithSourceMap.outputText, testSettings.expectedOutput + expectedSourceMappingUrlLine); } @@ -81,10 +81,10 @@ module ts { let suffix = getNewLineCharacter(transpileOptions.compilerOptions) + expectedSourceMappingUrlLine assert.isTrue(output.indexOf(suffix, output.length - suffix.length) !== -1); } - } + } } - + it("Generates correct compilerOptions diagnostics", () => { // Expecting 5047: "Option 'isolatedModules' can only be used when either option'--module' is provided or option 'target' is 'ES6' or higher." test(`var x = 0;`, { expectedDiagnosticCodes: [5047] }); @@ -97,7 +97,7 @@ module ts { it("Generates no diagnostics for missing file references", () => { test(`/// -var x = 0;`, +var x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS } } }); }); @@ -117,17 +117,17 @@ var x = 0;`, }); it("Generates module output", () => { - test(`var x = 0;`, - { - options: { compilerOptions: { module: ModuleKind.AMD } }, + test(`var x = 0;`, + { + options: { compilerOptions: { module: ModuleKind.AMD } }, expectedOutput: `define(["require", "exports"], function (require, exports) {\r\n "use strict";\r\n var x = 0;\r\n});\r\n` }); }); it("Uses correct newLine character", () => { - test(`var x = 0;`, - { - options: { compilerOptions: { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed } }, + test(`var x = 0;`, + { + options: { compilerOptions: { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed } }, expectedOutput: `"use strict";\nvar x = 0;\n` }); }); @@ -145,9 +145,9 @@ var x = 0;`, ` }\n` + ` }\n` + `});\n`; - test("var x = 1;", - { - options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, moduleName: "NamedModule" }, + test("var x = 1;", + { + options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, moduleName: "NamedModule" }, expectedOutput: output }) }); @@ -157,7 +157,7 @@ var x = 0;`, }); it("Rename dependencies - System", () => { - let input = + let input = `import {foo} from "SomeName";\n` + `declare function use(a: any);\n` + `use(foo);` @@ -177,15 +177,15 @@ var x = 0;`, ` }\n` + `});\n` - test(input, - { - options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }, + test(input, + { + options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }, expectedOutput: output }); }); it("Rename dependencies - AMD", () => { - let input = + let input = `import {foo} from "SomeName";\n` + `declare function use(a: any);\n` + `use(foo);` @@ -195,15 +195,15 @@ var x = 0;`, ` use(SomeName_1.foo);\n` + `});\n`; - test(input, - { - options: { compilerOptions: { module: ModuleKind.AMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }, + test(input, + { + options: { compilerOptions: { module: ModuleKind.AMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }, expectedOutput: output }); }); it("Rename dependencies - UMD", () => { - let input = + let input = `import {foo} from "SomeName";\n` + `declare function use(a: any);\n` + `use(foo);` @@ -221,15 +221,15 @@ var x = 0;`, ` use(SomeName_1.foo);\n` + `});\n`; - test(input, - { - options: { compilerOptions: { module: ModuleKind.UMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }, + test(input, + { + options: { compilerOptions: { module: ModuleKind.UMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }, expectedOutput: output }); }); - + it("Transpile with emit decorators and emit metadata", () => { - let input = + let input = `import {db} from './db';\n` + `function someDecorator(target) {\n` + ` return target;\n` + @@ -245,26 +245,26 @@ var x = 0;`, `export {MyClass}; \n` let output = `"use strict";\n` + - `var db_1 = require(\'./db\');\n` + + `var db_1 = require(\'./db\');\n` + `function someDecorator(target) {\n` + ` return target;\n` + - `}\n` + - `var MyClass = (function () {\n` + - ` function MyClass(db) {\n` + - ` this.db = db;\n` + - ` this.db.doSomething();\n` + - ` }\n` + - ` MyClass = __decorate([\n` + - ` someDecorator, \n` + - ` __metadata(\'design:paramtypes\', [(typeof (_a = typeof db_1.db !== \'undefined\' && db_1.db) === \'function\' && _a) || Object])\n` + - ` ], MyClass);\n` + - ` return MyClass;\n` + - ` var _a;\n` + - `}());\n` + - `exports.MyClass = MyClass;\n`; - - test(input, - { + `}\n` + + `var MyClass = (function () {\n` + + ` function MyClass(db) {\n` + + ` this.db = db;\n` + + ` this.db.doSomething();\n` + + ` }\n` + + ` return MyClass;\n` + + `}());\n` + + `MyClass = __decorate([\n` + + ` someDecorator, \n` + + ` __metadata(\'design:paramtypes\', [(typeof (_a = typeof db_1.db !== \'undefined\' && db_1.db) === \'function\' && _a) || Object])\n` + + `], MyClass);\n` + + `exports.MyClass = MyClass;\n` + + `var _a;\n`; + + test(input, + { options: { compilerOptions: { module: ModuleKind.CommonJS, @@ -274,7 +274,7 @@ var x = 0;`, experimentalDecorators: true, target: ScriptTarget.ES5, } - }, + }, expectedOutput: output }); }); @@ -282,7 +282,7 @@ var x = 0;`, it("Supports backslashes in file name", () => { test("var x", { expectedOutput: `"use strict";\r\nvar x;\r\n`, options: { fileName: "a\\b.ts" }}); }); - + it("transpile file as 'tsx' if 'jsx' is specified", () => { let input = `var x =
`; let output = `"use strict";\nvar x = React.createElement("div", null);\n`; From 359875b67de61c9acb68ae4f56540730f11e3508 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 29 Feb 2016 15:07:23 -0800 Subject: [PATCH 23/43] PR Feedback --- src/compiler/factory.ts | 2 +- src/compiler/transformers/ts.ts | 47 ++++++++++++++++++++------------- src/compiler/utilities.ts | 4 +++ 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index eae769fd562ab..ef908c40a9bb4 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -213,7 +213,7 @@ namespace ts { return name; } - export function createGeneratedNameForNode(node: Node, location?: TextRange): Identifier { + export function getGeneratedNameForNode(node: Node, location?: TextRange): Identifier { const name = createNode(SyntaxKind.Identifier, location); name.autoGenerateKind = GeneratedIdentifierKind.Node; name.original = node; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index d791dd1eb4cdb..e672a0a79cd6b 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -8,8 +8,6 @@ namespace ts { export function transformTypeScript(context: TransformationContext) { const { - getGeneratedNameForNode, - makeUniqueName, setNodeEmitFlags, startLexicalEnvironment, endLexicalEnvironment, @@ -496,7 +494,7 @@ namespace ts { // Record an alias to avoid class double-binding. if (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.ClassWithBodyScopedClassBinding) { enableExpressionSubstitutionForDecoratedClasses(); - decoratedClassAlias = makeUniqueName(node.name ? node.name.text : "default"); + decoratedClassAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? node.name.text : "default"); decoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAlias; // We emit the class alias as a `let` declaration here so that it has the same @@ -691,7 +689,7 @@ namespace ts { function transformConstructorParameters(constructor: ConstructorDeclaration, hasExtendsClause: boolean) { return constructor ? visitNodes(constructor.parameters, visitor, isParameter) - : hasExtendsClause ? [createRestParameter(makeUniqueName("args"))] : []; + : hasExtendsClause ? [createRestParameter(createUniqueName("args"))] : []; } /** @@ -2103,14 +2101,15 @@ namespace ts { } const savedCurrentNamespaceLocalName = currentNamespaceLocalName; - const modifiers = visitNodes(node.modifiers, visitor, isModifier); const statements: Statement[] = []; let location: TextRange = node; - if (!isNamespaceExport(node)) { + if (!isExported(node) || (isExternalModuleExport(node) && isFirstDeclarationOfKind(node, SyntaxKind.EnumDeclaration))) { + // Emit a VariableStatement if the enum is not exported, or is the first enum + // with the same name exported from an external module. addNode(statements, createVariableStatement( - modifiers, + visitNodes(node.modifiers, visitor, isModifier), createVariableDeclarationList([ createVariableDeclaration(node.name) ]), @@ -2179,7 +2178,7 @@ namespace ts { * * @param member The enum member node. */ - function transformEnumMember(member: EnumMember) { + function transformEnumMember(member: EnumMember): Statement { const name = getExpressionForPropertyName(member); return createStatement( createAssignment( @@ -2443,14 +2442,31 @@ namespace ts { && (isExternalModule(currentSourceFile) || !resolver.isTopLevelValueImportEqualsWithEntityName(node)); } + /** + * Gets a value indicating whether the node is exported. + * + * @param node The node to test. + */ + function isExported(node: Node) { + return (node.flags & NodeFlags.Export) !== 0; + } + + /** + * Gets a value indicating whether the node is exported from a namespace. + * + * @param node The node to test. + */ + function isNamespaceExport(node: Node) { + return currentNamespace !== undefined && isExported(node); + } + /** * Gets a value indicating whether the node is exported from an external module. * * @param node The node to test. */ function isExternalModuleExport(node: Node) { - return currentNamespace === undefined - && (node.flags & NodeFlags.Export) !== 0; + return currentNamespace === undefined && isExported(node); } /** @@ -2473,14 +2489,9 @@ namespace ts { && (node.flags & NodeFlags.Default) !== 0; } - /** - * Gets a value indicating whether the node is exported from a namespace. - * - * @param node The node to test. - */ - function isNamespaceExport(node: Node) { - return currentNamespace !== undefined - && (node.flags & NodeFlags.Export) !== 0; + function isFirstDeclarationOfKind(node: Declaration, kind: SyntaxKind) { + const original = getOriginalNode(node); + return !forEach(original.symbol && original.symbol.declarations, declaration => declaration.kind === kind && declaration.pos < original.pos); } /** diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b700498cd4d5d..1b707cedc4862 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2865,6 +2865,10 @@ namespace ts { return node.kind === SyntaxKind.Identifier; } + export function isGeneratedIdentifier(node: Node): node is Identifier { + return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.Node; + } + // Keywords export function isModifier(node: Node): node is Modifier { From fe7ad5fde3ec24e8caeb7a5ed712119e5606d541 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 29 Feb 2016 15:39:38 -0800 Subject: [PATCH 24/43] Minor tweaks to naming --- src/compiler/factory.ts | 20 +++++++++++++++----- src/compiler/transformers/ts.ts | 10 +++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index ef908c40a9bb4..0ad04c0a092f4 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -153,7 +153,21 @@ namespace ts { * Creates a shallow, memberwise clone of a node for mutation. */ export function getMutableNode(node: T): T { - return cloneNode(node, node, node.flags, node.parent, node); + 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); + } + + /** + * 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 { @@ -1340,8 +1354,4 @@ namespace ts { node.flags = flags; return node; } - - export function getSynthesizedNode(node: T): T { - return nodeIsSynthesized(node) ? node : cloneNode(node, /*location*/ undefined, node.flags, /*parent*/ undefined, /*original*/ node); - } } \ No newline at end of file diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index e672a0a79cd6b..b7a180aab9248 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1644,7 +1644,7 @@ namespace ts { return createLiteral(name.text); } else { - return getSynthesizedNode(name); + return getSynthesizedClone(name); } } @@ -1813,7 +1813,7 @@ namespace ts { if (isNamespaceExport(node)) { return createNodeArrayNode([ func, - createNamespaceExport(getSynthesizedNode(node.name), getSynthesizedNode(node.name)) + createNamespaceExport(getSynthesizedClone(node.name), getSynthesizedClone(node.name)) ]); } @@ -2420,7 +2420,7 @@ namespace ts { // exports.${name} = ${moduleReference}; return setOriginalNode( createNamespaceExport( - getSynthesizedNode(node.name), + getSynthesizedClone(node.name), moduleReference, node ), @@ -2520,14 +2520,14 @@ namespace ts { } function getNamespaceMemberName(name: Identifier): Expression { - name = getSynthesizedNode(name); + name = getSynthesizedClone(name); return currentNamespaceLocalName ? createPropertyAccess(currentNamespaceLocalName, name) : name; } function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) { - return node.name ? getSynthesizedNode(node.name) : getGeneratedNameForNode(node); + return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node); } function getClassPrototype(node: ClassExpression | ClassDeclaration) { From a0dbe7601cf3eb5fff2a5aa61c459cee868b6e25 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 29 Feb 2016 17:35:50 -0800 Subject: [PATCH 25/43] PR Feedback --- src/compiler/checker.ts | 6 +- src/compiler/emitter.ts | 6 +- src/compiler/printer.ts | 2 +- src/compiler/transformers/ts.ts | 458 ++++++++++++++++++-------------- src/compiler/types.ts | 4 +- src/compiler/utilities.ts | 5 +- 6 files changed, 269 insertions(+), 212 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3cb9031fd9913..278159469e192 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4846,7 +4846,7 @@ namespace ts { const parent = container && container.parent; if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) { if (!(container.flags & NodeFlags.Static) && - (container.kind !== SyntaxKind.Constructor || isNodeDescendentOf(node, (container).body))) { + (container.kind !== SyntaxKind.Constructor || isNodeDescendantOf(node, (container).body))) { return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent)).thisType; } } @@ -7206,8 +7206,8 @@ namespace ts { let container = getContainingClass(node); while (container !== undefined) { if (container === localOrExportSymbol.valueDeclaration && container.name !== node) { - getNodeLinks(container).flags |= NodeCheckFlags.ClassWithBodyScopedClassBinding; - getNodeLinks(node).flags |= NodeCheckFlags.BodyScopedClassBinding; + getNodeLinks(container).flags |= NodeCheckFlags.DecoratedClassWithSelfReference; + getNodeLinks(node).flags |= NodeCheckFlags.SelfReferenceInDecoratedClass; break; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a3c27d8d817a1..bda2bd79506cc 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -346,7 +346,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; function isUniqueLocalName(name: string, container: Node): boolean { - for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) { + for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) { if (node.locals && hasProperty(node.locals, name)) { // We conservatively include alias symbols to cover cases where they're emitted as locals if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { @@ -1529,7 +1529,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return; } } - else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) { + else if (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. @@ -5203,7 +5203,7 @@ const _super = (function (geti, seti) { // [Example 4] // - if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithBodyScopedClassBinding) { + if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.DecoratedClassWithSelfReference) { decoratedClassAlias = unescapeIdentifier(makeUniqueName(node.name ? node.name.text : "default")); decoratedClassAliases[getNodeId(node)] = decoratedClassAlias; write(`let ${decoratedClassAlias};`); diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index e44e88b46128d..331e502b9315b 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -2368,7 +2368,7 @@ const _super = (function (geti, seti) { } function isUniqueLocalName(name: string, container: Node): boolean { - for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) { + for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) { if (node.locals && hasProperty(node.locals, name)) { // We conservatively include alias symbols to cover cases where they're emitted as locals if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index b7a180aab9248..58f06cb214e3d 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -6,6 +6,15 @@ namespace ts { type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration; + const enum TypeScriptSubstitutionFlags { + /** Enables substitutions for decorated classes. */ + DecoratedClasses = 1 << 0, + /** Enables substitutions for namespace exports. */ + NamespaceExports = 1 << 1, + /** Enables substitutions for async methods with `super` calls. */ + AsyncMethodsWithSuper = 1 << 2, + } + export function transformTypeScript(context: TransformationContext) { const { setNodeEmitFlags, @@ -19,14 +28,14 @@ namespace ts { const languageVersion = getEmitScriptTarget(compilerOptions); // Save the previous transformation hooks. - const previousExpressionSubstitution = context.expressionSubstitution; const previousOnBeforeEmitNode = context.onBeforeEmitNode; const previousOnAfterEmitNode = context.onAfterEmitNode; + const previousExpressionSubstitution = context.expressionSubstitution; // Set new transformation hooks. - context.expressionSubstitution = substituteExpression; context.onBeforeEmitNode = onBeforeEmitNode; context.onAfterEmitNode = onAfterEmitNode; + context.expressionSubstitution = substituteExpression; // These variables contain state that changes as we descend into the tree. let currentSourceFile: SourceFile; @@ -36,31 +45,46 @@ namespace ts { let currentParent: Node; let currentNode: Node; - // These variables keep track of whether expression substitution has been enabled for - // specific edge cases. They are persisted between each SourceFile transformation and - // should not be reset. - let hasEnabledExpressionSubstitutionForDecoratedClasses = false; - let hasEnabledExpressionSubstitutionForNamespaceExports = false; - let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false; + /** + * Keeps track of whether expression substitution has been enabled for specific edge cases. + * They are persisted between each SourceFile transformation and should not be reset. + */ + let enabledSubstitutions: TypeScriptSubstitutionFlags; - // This map keeps track of aliases created for classes with decorators to avoid issues - // with the double-binding behavior of classes. + /** + * A map that keeps track of aliases created for classes with decorators to avoid issues + * with the double-binding behavior of classes. + */ let decoratedClassAliases: Map; - // This map keeps track of currently active aliases defined in `decoratedClassAliases` - // when just-in-time substitution occurs while printing an expression identifier. + /** + * A map that keeps track of currently active aliases defined in `decoratedClassAliases` + * when just-in-time substitution occurs while printing an expression identifier. + */ let currentDecoratedClassAliases: Map; - // This value keeps track of how deeply nested we are within any containing namespaces - // when performing just-in-time substitution while printing an expression identifier. + /** + * Keeps track of how deeply nested we are within any containing namespaces + * when performing just-in-time substitution while printing an expression identifier. + * If the nest level is greater than zero, then we are performing a substitution + * inside of a namespace and we should perform the more costly checks to determine + * whether the identifier points to an exported declaration. + */ let namespaceNestLevel: number; - // This array keeps track of containers where `super` is valid, for use with - // just-in-time substitution for `super` expressions inside of async methods. + /** + * This array keeps track of containers where `super` is valid, for use with + * just-in-time substitution for `super` expressions inside of async methods. + */ let superContainerStack: SuperContainer[]; return transformSourceFile; + /** + * Transform TypeScript-specific syntax in a SourceFile. + * + * @param node A SourceFile node. + */ function transformSourceFile(node: SourceFile) { currentSourceFile = node; node = visitEachChild(node, visitor, context); @@ -136,8 +160,7 @@ namespace ts { * @param node The node to visit. */ function namespaceElementVisitorWorker(node: Node): Node { - if (node.transformFlags & TransformFlags.TypeScript - || node.flags & NodeFlags.Export) { + 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); @@ -165,12 +188,25 @@ namespace ts { * @param node The node to visit. */ function classElementVisitorWorker(node: Node) { - if (node.kind === SyntaxKind.Constructor) { - // TypeScript constructors are elided. - return undefined; - } + switch (node.kind) { + case SyntaxKind.Constructor: + // TypeScript constructors are transformed in `transformClassDeclaration`. + // We elide them here as `visitorWorker` checks transform flags, which could + // erronously include an ES6 constructor without TypeScript syntax. + return undefined; - return visitorWorker(node); + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.IndexSignature: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.MethodDeclaration: + // Fallback to the default visit behavior. + return visitorWorker(node); + + default: + Debug.fail("Unexpected node."); + break; + } } /** @@ -179,9 +215,9 @@ namespace ts { * @param node The node to visit. */ function visitTypeScript(node: Node): Node { - // TypeScript ambient declarations are elided. if (node.flags & NodeFlags.Ambient) { - return; + // TypeScript ambient declarations are elided. + return undefined; } switch (node.kind) { @@ -233,8 +269,7 @@ namespace ts { // TypeScript property declarations are elided. case SyntaxKind.Constructor: - // TypeScript constructors are elided. The constructor of a class will be - // transformed as part of `transformClassDeclaration`. + // TypeScript constructors are transformed in `transformClassDeclaration`. return undefined; case SyntaxKind.ClassDeclaration: @@ -376,11 +411,7 @@ namespace ts { */ function visitClassDeclaration(node: ClassDeclaration): NodeArrayNode { const staticProperties = getInitializedProperties(node, /*isStatic*/ true); - const statements: Statement[] = []; - const modifiers = visitNodes(node.modifiers, visitor, isModifier); - const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); - const members = transformClassMembers(node, heritageClauses !== undefined); - let decoratedClassAlias: Identifier; + const hasExtendsClause = getClassExtendsHeritageClauseElement(node) !== undefined; // emit name if // - node has a name @@ -391,166 +422,28 @@ namespace ts { name = getGeneratedNameForNode(node); } - if (node.decorators) { - // When we emit an ES6 class that has a class decorator, we must tailor the - // emit to certain specific cases. - // - // In the simplest case, we emit the class declaration as a let declaration, and - // evaluate decorators after the close of the class body: - // - // [Example 1] - // --------------------------------------------------------------------- - // TypeScript | Javascript - // --------------------------------------------------------------------- - // @dec | let C = class C { - // class C { | } - // } | C = __decorate([dec], C); - // --------------------------------------------------------------------- - // @dec | let C = class C { - // export class C { | } - // } | C = __decorate([dec], C); - // | export { C }; - // --------------------------------------------------------------------- - // - // If a class declaration contains a reference to itself *inside* of the class body, - // this introduces two bindings to the class: One outside of the class body, and one - // inside of the class body. If we apply decorators as in [Example 1] above, there - // is the possibility that the decorator `dec` will return a new value for the - // constructor, which would result in the binding inside of the class no longer - // pointing to the same reference as the binding outside of the class. - // - // As a result, we must instead rewrite all references to the class *inside* of the - // class body to instead point to a local temporary alias for the class: - // - // [Example 2] - // --------------------------------------------------------------------- - // TypeScript | Javascript - // --------------------------------------------------------------------- - // @dec | let C_1; - // class C { | let C = C_1 = class C { - // static x() { return C.y; } | static x() { return C_1.y; } - // static y = 1; | } - // } | C.y = 1; - // | C = C_1 = __decorate([dec], C); - // --------------------------------------------------------------------- - // @dec | let C_1; - // export class C { | let C = C_1 = class C { - // static x() { return C.y; } | static x() { return C_1.y; } - // static y = 1; | } - // } | C.y = 1; - // | C = C_1 = __decorate([dec], C); - // | export { C }; - // --------------------------------------------------------------------- - // - // If a class declaration is the default export of a module, we instead emit - // the export after the decorated declaration: - // - // [Example 3] - // --------------------------------------------------------------------- - // TypeScript | Javascript - // --------------------------------------------------------------------- - // @dec | let default_1 = class { - // export default class { | } - // } | default_1 = __decorate([dec], default_1); - // | export default default_1; - // --------------------------------------------------------------------- - // @dec | let C = class C { - // export default class C { | } - // } | C = __decorate([dec], C); - // | export default C; - // --------------------------------------------------------------------- - // - // If the class declaration is the default export and a reference to itself - // inside of the class body, we must emit both an alias for the class *and* - // move the export after the declaration: - // - // [Example 4] - // --------------------------------------------------------------------- - // TypeScript | Javascript - // --------------------------------------------------------------------- - // @dec | let C_1; - // export default class C { | let C = C_1 = class C { - // static x() { return C.y; } | static x() { return C_1.y; } - // static y = 1; | } - // } | C.y = 1; - // | C = C_1 = __decorate([dec], C); - // | export default C; - // --------------------------------------------------------------------- - // - - // class ${name} ${heritageClauses} { - // ${members} - // } - let classExpression: Expression = setOriginalNode( - createClassExpression( - name, - heritageClauses, - members, - /*location*/ node - ), - node - ); - - // Record an alias to avoid class double-binding. - if (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.ClassWithBodyScopedClassBinding) { - enableExpressionSubstitutionForDecoratedClasses(); - decoratedClassAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? node.name.text : "default"); - decoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAlias; - - // We emit the class alias as a `let` declaration here so that it has the same - // TDZ as the class. - - // let ${decoratedClassAlias}; - addNode(statements, - createVariableStatement( - /*modifiers*/ undefined, - createVariableDeclarationList([ - createVariableDeclaration(decoratedClassAlias) - ], - /*location*/ undefined, - NodeFlags.Let) - ) - ); - - // ${decoratedClassAlias} = ${classExpression} - classExpression = createAssignment( - cloneNode(decoratedClassAlias), - classExpression, - /*location*/ node); - } - - // let ${name} = ${classExpression}; - addNode(statements, - createVariableStatement( - /*modifiers*/ undefined, - createVariableDeclarationList([ - createVariableDeclaration( - name, - classExpression - ) - ], - /*location*/ undefined, - NodeFlags.Let) - ) - ); - } - else { + let decoratedClassAlias: Identifier; + const statements: Statement[] = []; + if (!node.decorators) { // ${modifiers} class ${name} ${heritageClauses} { // ${members} // } addNode(statements, setOriginalNode( createClassDeclaration( - modifiers, + visitNodes(node.modifiers, visitor, isModifier), name, - heritageClauses, - members, + visitNodes(node.heritageClauses, visitor, isHeritageClause), + transformClassMembers(node, hasExtendsClause), /*location*/ node ), node ) ); } + else { + decoratedClassAlias = addClassDeclarationHeadWithDecorators(statements, node, name, hasExtendsClause); + } // Emit static property assignment. Because classDeclaration is lexically evaluated, // it is safe to emit static property assignment after classDeclaration @@ -575,13 +468,168 @@ namespace ts { addNode(statements, createExportDefault(name)); } else if (isNamedExternalModuleExport(node)) { - addNode(statements, createModuleExport(name)); + addNode(statements, createExternalModuleExport(name)); } } return createNodeArrayNode(statements); } + /** + * Transforms a decorated class declaration and appends the resulting statements. If + * the class requires an alias to avoid issues with double-binding, the alias is returned. + * + * @param node A ClassDeclaration node. + * @param name The name of the class. + * @param hasExtendsClause A value indicating whether + */ + function addClassDeclarationHeadWithDecorators(statements: Statement[], node: ClassDeclaration, name: Identifier, hasExtendsClause: boolean) { + // When we emit an ES6 class that has a class decorator, we must tailor the + // emit to certain specific cases. + // + // In the simplest case, we emit the class declaration as a let declaration, and + // evaluate decorators after the close of the class body: + // + // [Example 1] + // --------------------------------------------------------------------- + // TypeScript | Javascript + // --------------------------------------------------------------------- + // @dec | let C = class C { + // class C { | } + // } | C = __decorate([dec], C); + // --------------------------------------------------------------------- + // @dec | let C = class C { + // export class C { | } + // } | C = __decorate([dec], C); + // | export { C }; + // --------------------------------------------------------------------- + // + // If a class declaration contains a reference to itself *inside* of the class body, + // this introduces two bindings to the class: One outside of the class body, and one + // inside of the class body. If we apply decorators as in [Example 1] above, there + // is the possibility that the decorator `dec` will return a new value for the + // constructor, which would result in the binding inside of the class no longer + // pointing to the same reference as the binding outside of the class. + // + // As a result, we must instead rewrite all references to the class *inside* of the + // class body to instead point to a local temporary alias for the class: + // + // [Example 2] + // --------------------------------------------------------------------- + // TypeScript | Javascript + // --------------------------------------------------------------------- + // @dec | let C_1; + // class C { | let C = C_1 = class C { + // static x() { return C.y; } | static x() { return C_1.y; } + // static y = 1; | } + // } | C.y = 1; + // | C = C_1 = __decorate([dec], C); + // --------------------------------------------------------------------- + // @dec | let C_1; + // export class C { | let C = C_1 = class C { + // static x() { return C.y; } | static x() { return C_1.y; } + // static y = 1; | } + // } | C.y = 1; + // | C = C_1 = __decorate([dec], C); + // | export { C }; + // --------------------------------------------------------------------- + // + // If a class declaration is the default export of a module, we instead emit + // the export after the decorated declaration: + // + // [Example 3] + // --------------------------------------------------------------------- + // TypeScript | Javascript + // --------------------------------------------------------------------- + // @dec | let default_1 = class { + // export default class { | } + // } | default_1 = __decorate([dec], default_1); + // | export default default_1; + // --------------------------------------------------------------------- + // @dec | let C = class C { + // export default class C { | } + // } | C = __decorate([dec], C); + // | export default C; + // --------------------------------------------------------------------- + // + // If the class declaration is the default export and a reference to itself + // inside of the class body, we must emit both an alias for the class *and* + // move the export after the declaration: + // + // [Example 4] + // --------------------------------------------------------------------- + // TypeScript | Javascript + // --------------------------------------------------------------------- + // @dec | let C_1; + // export default class C { | let C = C_1 = class C { + // static x() { return C.y; } | static x() { return C_1.y; } + // static y = 1; | } + // } | C.y = 1; + // | C = C_1 = __decorate([dec], C); + // | export default C; + // --------------------------------------------------------------------- + // + + // ... = class ${name} ${heritageClauses} { + // ${members} + // } + let classExpression: Expression = setOriginalNode( + createClassExpression( + name, + visitNodes(node.heritageClauses, visitor, isHeritageClause), + transformClassMembers(node, hasExtendsClause), + /*location*/ node + ), + node + ); + + // Record an alias to avoid class double-binding. + let decoratedClassAlias: Identifier; + if (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.DecoratedClassWithSelfReference) { + enableExpressionSubstitutionForDecoratedClasses(); + decoratedClassAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? node.name.text : "default"); + decoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAlias; + + // We emit the class alias as a `let` declaration here so that it has the same + // TDZ as the class. + + // let ${decoratedClassAlias}; + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration(decoratedClassAlias) + ], + /*location*/ undefined, + NodeFlags.Let) + ) + ); + + // ${decoratedClassAlias} = ${classExpression} + classExpression = createAssignment( + decoratedClassAlias, + classExpression, + /*location*/ node); + } + + // let ${name} = ${classExpression}; + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + name, + classExpression + ) + ], + /*location*/ undefined, + NodeFlags.Let) + ) + ); + + return decoratedClassAlias; + } + /** * Transforms a class expression with TypeScript syntax into compatible ES6. * @@ -638,7 +686,7 @@ namespace ts { const members: ClassElement[] = []; addNode(members, transformConstructor(node, hasExtendsClause)); addNodes(members, visitNodes(node.members, classElementVisitor, isClassElement)); - return members; + return createNodeArray(members, /*location*/ node.members); } /** @@ -2489,9 +2537,15 @@ namespace ts { && (node.flags & NodeFlags.Default) !== 0; } + /** + * Gets a value indicating whether a node is the first declaration of its kind. + * + * @param node A Declaration node. + * @param kind The SyntaxKind to find among related declarations. + */ function isFirstDeclarationOfKind(node: Declaration, kind: SyntaxKind) { const original = getOriginalNode(node); - return !forEach(original.symbol && original.symbol.declarations, declaration => declaration.kind === kind && declaration.pos < original.pos); + return original.symbol && getDeclarationOfKind(original.symbol, kind) === original; } /** @@ -2511,7 +2565,7 @@ namespace ts { ); } - function createModuleExport(exportName: Identifier) { + function createExternalModuleExport(exportName: Identifier) { return createExportDeclaration( createNamedExports([ createExportSpecifier(exportName) @@ -2544,12 +2598,13 @@ namespace ts { previousOnBeforeEmitNode(node); const kind = node.kind; - if (hasEnabledExpressionSubstitutionForDecoratedClasses - && kind === SyntaxKind.ClassDeclaration && node.decorators) { + if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses + && kind === SyntaxKind.ClassDeclaration + && node.decorators) { currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)]; } - if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper + if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && (kind === SyntaxKind.ClassDeclaration || kind === SyntaxKind.Constructor || kind === SyntaxKind.MethodDeclaration @@ -2563,7 +2618,7 @@ namespace ts { superContainerStack.push(node); } - if (hasEnabledExpressionSubstitutionForNamespaceExports + if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && kind === SyntaxKind.ModuleDeclaration) { namespaceNestLevel++; } @@ -2573,12 +2628,13 @@ namespace ts { previousOnAfterEmitNode(node); const kind = node.kind; - if (hasEnabledExpressionSubstitutionForDecoratedClasses - && kind === SyntaxKind.ClassDeclaration && node.decorators) { + if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses + && kind === SyntaxKind.ClassDeclaration + && node.decorators) { currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined; } - if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper + if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && (kind === SyntaxKind.ClassDeclaration || kind === SyntaxKind.Constructor || kind === SyntaxKind.MethodDeclaration @@ -2590,7 +2646,7 @@ namespace ts { } } - if (hasEnabledExpressionSubstitutionForNamespaceExports + if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && kind === SyntaxKind.ModuleDeclaration) { namespaceNestLevel--; } @@ -2604,7 +2660,7 @@ namespace ts { return substituteExpressionIdentifier(node); } - if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) { + if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) { switch (node.kind) { case SyntaxKind.CallExpression: return substituteCallExpression(node); @@ -2619,9 +2675,9 @@ namespace ts { } function substituteExpressionIdentifier(node: Identifier): Expression { - if (hasEnabledExpressionSubstitutionForDecoratedClasses + if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses && !nodeIsSynthesized(node) - && resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) { + && 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. @@ -2635,7 +2691,7 @@ namespace ts { } } - if (hasEnabledExpressionSubstitutionForNamespaceExports + if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && namespaceNestLevel > 0) { // If we are nested within a namespace declaration, we may need to qualifiy // an identifier that is exported from a merged namespace. @@ -2702,8 +2758,8 @@ namespace ts { } function enableExpressionSubstitutionForAsyncMethodsWithSuper() { - if (!hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) { - hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = true; + if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) === 0) { + enabledSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports; // We need to enable substitutions for call, property access, and element access // if we need to rewrite super calls. @@ -2721,8 +2777,8 @@ namespace ts { } function enableExpressionSubstitutionForDecoratedClasses() { - if (!hasEnabledExpressionSubstitutionForDecoratedClasses) { - hasEnabledExpressionSubstitutionForDecoratedClasses = true; + if ((enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses) === 0) { + enabledSubstitutions |= TypeScriptSubstitutionFlags.DecoratedClasses; // We need to enable substitutions for identifiers. This allows us to // substitute class names inside of a class declaration. @@ -2735,8 +2791,8 @@ namespace ts { } function enableExpressionSubstitutionForNamespaceExports() { - if (!hasEnabledExpressionSubstitutionForNamespaceExports) { - hasEnabledExpressionSubstitutionForNamespaceExports = true; + if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) === 0) { + enabledSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports; // We need to enable substitutions for identifiers. This allows us to // substitute the names of exported members of a namespace. diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 07d1daec6699c..5cced242498b2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2091,8 +2091,8 @@ namespace ts { CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement HasSeenSuperCall = 0x00080000, // Set during the binding when encounter 'super' - ClassWithBodyScopedClassBinding = 0x00100000, // Decorated class that contains a binding to itself inside of the class body. - BodyScopedClassBinding = 0x00200000, // Binding to a decorated class inside of the class's body. + DecoratedClassWithSelfReference = 0x00100000, // Decorated class that contains a binding to itself inside of the class body. + SelfReferenceInDecoratedClass = 0x00200000, // Binding to a decorated class inside of the class's body. } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1b707cedc4862..421737553d4b8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1352,7 +1352,7 @@ namespace ts { - export function isNodeDescendentOf(node: Node, ancestor: Node): boolean { + export function isNodeDescendantOf(node: Node, ancestor: Node): boolean { while (node) { if (node === ancestor) return true; node = node.parent; @@ -2866,7 +2866,8 @@ namespace ts { } export function isGeneratedIdentifier(node: Node): node is Identifier { - return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.Node; + // Using `>` here catches both `GeneratedIdentifierKind.None` and `undefined`. + return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.None; } // Keywords From 7d05ba28bf7979c94d4eac7b586ce7c13c6fdfef Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 1 Mar 2016 15:16:41 -0800 Subject: [PATCH 26/43] Fixed visitJsxText, plus PR Feedback --- src/compiler/core.ts | 90 ++++++++++- src/compiler/transformers/jsx.ts | 251 ++++++++++++++++--------------- src/compiler/utilities.ts | 4 + 3 files changed, 216 insertions(+), 129 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a464bd8668240..15a8ce87eab73 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -171,23 +171,99 @@ 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; + } + + /** + * 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; } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index cb1aef32a9799..924cfbfdb9015 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -9,6 +9,11 @@ namespace ts { 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); } @@ -64,7 +69,6 @@ namespace ts { } function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: JsxChild[]) { - // We must the node onto the node stack if it is not already at the top. const tagName = getTagName(node); let objectProperties: Expression; if (node.attributes.length === 0) { @@ -75,41 +79,35 @@ namespace ts { // Either emit one big object literal (no spread attribs), or // a call to React.__spread const attrs = node.attributes; - if (forEach(attrs, isJsxSpreadAttribute)) { - const segments: Expression[] = []; - let properties: ObjectLiteralElement[] = []; - for (const attr of attrs) { - if (isJsxSpreadAttribute(attr)) { - if (properties) { - segments.push(createObjectLiteral(properties)); - properties = undefined; - } - - addNode(segments, transformJsxSpreadAttributeToExpression(attr)); - } - else { - if (!properties) { - properties = []; - } - - addNode(properties, transformJsxAttributeToObjectLiteralElement(attr)); - } - } - - if (properties) { - segments.push(createObjectLiteral(properties)); - } - - objectProperties = createJsxSpread(compilerOptions.reactNamespace, segments); + if (!forEach(attrs, isJsxSpreadAttribute)) { + objectProperties = createObjectLiteral(map(node.attributes, transformJsxAttributeToObjectLiteralElement)); } else { - const properties = map(node.attributes, transformJsxAttributeToObjectLiteralElement); - objectProperties = createObjectLiteral(properties); + objectProperties = createJsxSpread(compilerOptions.reactNamespace, + concatenate( + // We must always emit at least one object literal before a spread + // argument. + isJsxSpreadAttribute(attrs[0]) ? [createObjectLiteral()] : undefined, + + // Map spans of JsxAttribute nodes into object literals and spans + // of JsxSpreadAttribute nodes into expressions. + flatten( + spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread + ? map(attrs, transformJsxSpreadAttributeToExpression) + : createObjectLiteral(map(attrs, transformJsxAttributeToObjectLiteralElement)) + ) + ) + ) + ); } } - const childExpressions = filter(map(children, transformJsxChildToExpression), isDefined); - return createJsxCreateElement(compilerOptions.reactNamespace, tagName, objectProperties, childExpressions); + return createJsxCreateElement( + compilerOptions.reactNamespace, + tagName, + objectProperties, + filter(map(children, transformJsxChildToExpression), isDefined) + ); } function transformJsxSpreadAttributeToExpression(node: JsxSpreadAttribute) { @@ -125,39 +123,29 @@ namespace ts { } function visitJsxText(node: JsxText) { - const text = getTextToEmit(node); - if (text !== undefined) { - return createLiteral(text); - } - return undefined; - } - - function getTextToEmit(node: JsxText) { - const text = trimReactWhitespaceAndApplyEntities(node); - if (text === undefined || text.length === 0) { - return undefined; - } - else { - return text; - } - } - - function trimReactWhitespaceAndApplyEntities(node: JsxText): string { const text = getTextOfNode(node, /*includeTrivia*/ true); - let result: string = undefined; + 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 + // 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); - result = (result ? result + "\" + ' ' + \"" : "") + part; + 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)) { @@ -170,22 +158,41 @@ namespace ts { if (firstNonWhitespace !== -1) { const part = text.substr(firstNonWhitespace); - result = (result ? result + "\" + ' ' + \"" : "") + part; + 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 (result) { - // Replace entities like   - result = result.replace(/&(\w+);/g, function(s: any, m: string) { - if (entities[m] !== undefined) { - return String.fromCharCode(entities[m]); - } - else { - return s; - } - }); + if (parts) { + return reduceLeft(parts, aggregateJsxTextParts); } - return result; + 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 { @@ -210,7 +217,7 @@ namespace ts { */ function getAttributeName(node: JsxAttribute): StringLiteral | Identifier { const name = node.name; - if (/[A-Za-z_]+[\w*]/.test(name.text)) { + if (/^[A-Za-z_]\w*$/.test(name.text)) { return createLiteral(name.text); } else { @@ -422,62 +429,62 @@ namespace ts { "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 - }; + "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/utilities.ts b/src/compiler/utilities.ts index 421737553d4b8..80cd6c7f8adb8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3268,6 +3268,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 { From 186f5c8bca792659aabc69c21d9253a6c18256f3 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 1 Mar 2016 15:26:29 -0800 Subject: [PATCH 27/43] PR Feedback --- src/compiler/transformers/jsx.ts | 42 +++++++++++++++----------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 924cfbfdb9015..c5085da529543 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -71,35 +71,31 @@ namespace ts { function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: JsxChild[]) { const tagName = getTagName(node); let objectProperties: Expression; - if (node.attributes.length === 0) { + 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 - const attrs = node.attributes; - if (!forEach(attrs, isJsxSpreadAttribute)) { - objectProperties = createObjectLiteral(map(node.attributes, transformJsxAttributeToObjectLiteralElement)); - } - else { - objectProperties = createJsxSpread(compilerOptions.reactNamespace, - concatenate( - // We must always emit at least one object literal before a spread - // argument. - isJsxSpreadAttribute(attrs[0]) ? [createObjectLiteral()] : undefined, - - // Map spans of JsxAttribute nodes into object literals and spans - // of JsxSpreadAttribute nodes into expressions. - flatten( - spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread - ? map(attrs, transformJsxSpreadAttributeToExpression) - : createObjectLiteral(map(attrs, transformJsxAttributeToObjectLiteralElement)) - ) - ) - ) - ); - } + objectProperties = singleOrUndefined(segments) + || createJsxSpread(compilerOptions.reactNamespace, segments); } return createJsxCreateElement( From 30433c2c67cf8073b087e8ac90e36d1cf3a61c6b Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 2 Mar 2016 11:38:30 -0800 Subject: [PATCH 28/43] ES6 cleanup --- src/compiler/binder.ts | 21 +- src/compiler/checker.ts | 2 +- src/compiler/core.ts | 17 + src/compiler/emitter.ts | 2 +- src/compiler/factory.ts | 111 ++- src/compiler/transformers/es6.ts | 1363 ++++++++++++++++++++---------- src/compiler/transformers/ts.ts | 7 +- src/compiler/types.ts | 21 +- src/compiler/utilities.ts | 26 +- src/compiler/visitor.ts | 4 +- 10 files changed, 1060 insertions(+), 514 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4ea2489de8886..f1b6e0053bfaa 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1930,7 +1930,8 @@ namespace ts { case SyntaxKind.CallExpression: excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes; if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression - || isSuperCall(node)) { + || 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; @@ -1969,12 +1970,18 @@ namespace ts { } } + // 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; + } + break; case SyntaxKind.BinaryExpression: if (isDestructuringAssignment(node)) { // Destructuring assignments are ES6 syntax. - transformFlags |= TransformFlags.AssertES6; + transformFlags |= TransformFlags.AssertES6 | TransformFlags.DestructuringAssignment; } else if ((node).operatorToken.kind === SyntaxKind.AsteriskAsteriskToken || (node).operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { @@ -1984,6 +1991,16 @@ namespace ts { break; + 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) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b7a1bd0b05d26..cf90007062e14 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7495,7 +7495,7 @@ namespace ts { // 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 (isSuperProperty(node.parent) && isAssignmentTarget(node.parent)) { getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding; } else { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 15a8ce87eab73..aeb908c20cc72 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -220,6 +220,23 @@ namespace ts { 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. * diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index bda2bd79506cc..d279c419b530a 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); } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index ee886fa334eee..c4813ab13c464 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -152,7 +152,7 @@ namespace ts { /** * Creates a shallow, memberwise clone of a node for mutation. */ - export function getMutableNode(node: T): T { + export function getMutableClone(node: T): T { return cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); } @@ -320,15 +320,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 +362,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 +375,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 +384,10 @@ namespace ts { node.parameters = createNodeArray(parameters); node.type = undefined; node.body = body; + if (original) { + node.original = original; + } + return node; } @@ -468,9 +478,12 @@ 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; } @@ -573,7 +586,7 @@ 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); @@ -583,6 +596,9 @@ namespace ts { node.parameters = createNodeArray(parameters); node.type = undefined; node.body = body; + if (original) { + node.original = original; + } return node; } @@ -1068,6 +1084,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 (isSuperCall(callee)) { + 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); } @@ -1206,6 +1284,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. diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 73282e92ec38d..4db823266c9fd 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -3,14 +3,20 @@ /*@internal*/ namespace ts { + + 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, setNodeEmitFlags, - enableExpressionSubstitution, - enableEmitNotification, } = context; const resolver = context.getEmitResolver(); @@ -18,24 +24,43 @@ namespace ts { const previousExpressionSubstitution = context.expressionSubstitution; const previousOnBeforeEmitNode = context.onBeforeEmitNode; const previousOnAfterEmitNode = context.onAfterEmitNode; - context.enableExpressionSubstitution(SyntaxKind.Identifier); - context.identifierSubstitution = substituteIdentifier; - context.expressionSubstitution = substituteExpression; context.onBeforeEmitNode = onBeforeEmitNode; context.onAfterEmitNode = onAfterEmitNode; + context.identifierSubstitution = substituteIdentifier; + context.expressionSubstitution = substituteExpression; let currentSourceFile: SourceFile; let currentParent: Node; let currentNode: Node; let enclosingBlockScopeContainer: Node; let enclosingBlockScopeContainerParent: Node; - let containingFunction: FunctionLikeDeclaration; let containingNonArrowFunction: FunctionLikeDeclaration; - let combinedNodeFlags: NodeFlags; - // This stack is is used to support substitutions when printing nodes. - let hasEnabledExpressionSubstitutionForCapturedThis = false; - let containingFunctionStack: 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; + + /** + * Keeps track of how deeply nested we are within function-likes when printing + * nodes. This is used to determine whether we need to emit `_this` instead of + * `this`. + */ + let containingFunctionDepth: number; + + /** + * The first 31 bits are used to determine whether a containing function is an + * arrow function. + */ + let containingFunctionState: number; + + /** + * If the containingFunctionDepth grows beyond 31 nested function-likes, this + * array is used as a stack to track deeper levels of nesting. + */ + let containingFunctionStack: number[]; return transformSourceFile; @@ -45,24 +70,20 @@ namespace ts { } function visitor(node: Node): Node { - const savedContainingFunction = containingFunction; const savedContainingNonArrowFunction = containingNonArrowFunction; const savedCurrentParent = currentParent; const savedCurrentNode = currentNode; const savedEnclosingBlockScopeContainer = enclosingBlockScopeContainer; const savedEnclosingBlockScopeContainerParent = enclosingBlockScopeContainerParent; - const savedCombinedNodeFlags = combinedNodeFlags; onBeforeVisitNode(node); node = visitorWorker(node); - containingFunction = savedContainingFunction; containingNonArrowFunction = savedContainingNonArrowFunction; currentParent = savedCurrentParent; currentNode = savedCurrentNode; enclosingBlockScopeContainer = savedEnclosingBlockScopeContainer; enclosingBlockScopeContainerParent = savedEnclosingBlockScopeContainerParent; - combinedNodeFlags = savedCombinedNodeFlags; return node; } @@ -122,6 +143,9 @@ namespace ts { case SyntaxKind.ForOfStatement: return visitForOfStatement(node); + case SyntaxKind.ExpressionStatement: + return visitExpressionStatement(node); + case SyntaxKind.ObjectLiteralExpression: return visitObjectLiteralExpression(node); @@ -137,8 +161,11 @@ namespace ts { case SyntaxKind.NewExpression: return visitNewExpression(node); + case SyntaxKind.ParenthesizedExpression: + return visitParenthesizedExpression(node, /*needsDestructuringValue*/ true); + case SyntaxKind.BinaryExpression: - return visitBinaryExpression(node); + return visitBinaryExpression(node, /*needsDestructuringValue*/ true); case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.TemplateHead: @@ -170,8 +197,6 @@ namespace ts { currentParent = currentNode; currentNode = node; - combinedNodeFlags = combineNodeFlags(currentNode, currentParent, combinedNodeFlags); - if (currentParent) { if (isBlockScope(currentParent, currentGrandparent)) { enclosingBlockScopeContainer = currentParent; @@ -186,17 +211,27 @@ namespace ts { case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: containingNonArrowFunction = currentParent; - containingFunction = currentParent; - break; - - case SyntaxKind.ArrowFunction: - containingFunction = currentParent; break; } } } + /** + * Visits a ClassDeclaration and transforms it into a variable statement. + * + * @parma 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, @@ -211,11 +246,54 @@ namespace ts { ); } + /** + * 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( @@ -225,25 +303,40 @@ namespace ts { baseTypeNode ? [createParameter("_super")] : [], transformClassBody(node, baseTypeNode !== undefined) ), - baseTypeNode ? [visitNode(baseTypeNode.expression, visitor, isExpression)] : [] + 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); - addNode(statements, createReturn(getDeclarationName(node))); - addNodes(statements, endLexicalEnvironment()); - return setMultiLine(createBlock(statements), /*multiLine*/ true); - } - - function addExtendsHelperIfNeeded(classStatements: Statement[], node: ClassExpression | ClassDeclaration, hasExtendsClause: boolean): void { + 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) { - addNode(classStatements, + statements.push( createStatement( createExtendsHelper(getDeclarationName(node)) ) @@ -251,10 +344,17 @@ namespace ts { } } - function addConstructor(classStatements: Statement[], node: ClassExpression | ClassDeclaration, hasExtendsClause: boolean): void { + /** + * 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); - addNode(classStatements, + statements.push( createFunctionDeclaration( /*modifiers*/ undefined, /*asteriskToken*/ undefined, @@ -266,7 +366,19 @@ namespace ts { ); } + /** + * 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); } @@ -274,28 +386,50 @@ namespace ts { 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); - addDefaultValueAssignments(statements, constructor); - addRestParameter(statements, constructor, hasSynthesizedSuper); + addDefaultValueAssignmentsIfNeeded(statements, constructor); + addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper); } - addDefaultSuperCall(statements, constructor, hasExtendsClause, hasSynthesizedSuper); + addDefaultSuperCallIfNeeded(statements, constructor, hasExtendsClause, hasSynthesizedSuper); if (constructor) { - addNodes(statements, visitNodes(constructor.body.statements, visitor, isStatement, hasSynthesizedSuper ? 1 : 0)); - } - - addNodes(statements, endLexicalEnvironment()); - return setMultiLine(createBlock(statements, /*location*/ constructor && constructor.body), /*multiLine*/ true); - } - - function addDefaultSuperCall(statements: Statement[], constructor: ConstructorDeclaration, hasExtendsClause: boolean, hasSynthesizedSuper: boolean) { + 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) { - addNode(statements, + statements.push( createStatement( createFunctionApply( createIdentifier("_super"), @@ -307,6 +441,11 @@ namespace ts { } } + /** + * 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 @@ -334,11 +473,24 @@ namespace ts { } } + /** + * 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; } - function addDefaultValueAssignments(statements: Statement[], node: FunctionLikeDeclaration): void { + /** + * 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; } @@ -361,6 +513,14 @@ namespace ts { } } + /** + * 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); @@ -368,17 +528,17 @@ namespace ts { // 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) { - addNode(statements, + statements.push( createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList( - transformParameterBindingElements(parameter, temp) + flattenParameterDestructuring(parameter, temp, visitor) ) ) ); } else if (initializer) { - addNode(statements, + statements.push( createStatement( createAssignment( temp, @@ -389,12 +549,16 @@ namespace ts { } } - function transformParameterBindingElements(parameter: ParameterDeclaration, name: Identifier) { - return flattenParameterDestructuring(parameter, name, visitor); - } - + /** + * 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 { - addNode(statements, + statements.push( createIf( createStrictEquality( getSynthesizedClone(name), @@ -415,17 +579,30 @@ namespace ts { ); } - function shouldAddRestParameter(node: ParameterDeclaration) { - return node && node.dotDotDotToken; - } - - function addRestParameter(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper?: boolean): void { - if (inConstructorWithSynthesizedSuper) { - return; - } - + /** + * 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)) { + if (!shouldAddRestParameter(parameter, inConstructorWithSynthesizedSuper)) { return; } @@ -434,7 +611,7 @@ namespace ts { const temp = createLoopVariable(); // var param = []; - addNode(statements, + statements.push( createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList([ @@ -449,7 +626,7 @@ namespace ts { // for (var _i = restIndex; _i < arguments.length; _i++) { // param[_i - restIndex] = arguments[_i]; // } - addNode(statements, + statements.push( createFor( createVariableDeclarationList([ createVariableDeclaration(temp, createLiteral(restIndex)) @@ -465,7 +642,7 @@ namespace ts { createAssignment( createElementAccess( name, - restIndex === 0 ? temp : createSubtract(temp, createLiteral(restIndex)) + createSubtract(temp, createLiteral(restIndex)) ), createElementAccess(createIdentifier("arguments"), temp) ) @@ -476,11 +653,16 @@ namespace ts { ); } + /** + * 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) { - enableExpressionSubstitutionForCapturedThis(); - - addNode(statements, + enableSubstitutionsForCapturedThis(); + statements.push( createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList([ @@ -494,23 +676,29 @@ namespace ts { } } - function addClassMembers(classStatements: Statement[], node: ClassExpression | ClassDeclaration): void { + /** + * 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: - addNode(classStatements, transformSemicolonClassElementToStatement(member)); + statements.push(transformSemicolonClassElementToStatement(member)); break; case SyntaxKind.MethodDeclaration: - addNode(classStatements, transformClassMethodDeclarationToStatement(node, member)); + statements.push(transformClassMethodDeclarationToStatement(getClassMemberPrefix(node, member), member)); break; case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: const accessors = getAllAccessorDeclarations(node.members, member); if (member === accessors.firstAccessor) { - const receiver = getClassMemberPrefix(node, member); - addNode(classStatements, transformAccessorsToStatement(receiver, accessors)); + statements.push(transformAccessorsToStatement(getClassMemberPrefix(node, member), accessors)); } break; @@ -526,42 +714,52 @@ namespace ts { } } + /** + * Transforms a SemicolonClassElement into a statement for a class body function. + * + * @param member The SemicolonClassElement node. + */ function transformSemicolonClassElementToStatement(member: SemicolonClassElement) { - return createEmptyStatement(member); + return createEmptyStatement(/*location*/ member); } - function transformClassMethodDeclarationToStatement(node: ClassExpression | ClassDeclaration, member: MethodDeclaration) { - const savedContainingFunction = containingFunction; - const savedContainingNonArrowFunction = containingNonArrowFunction; - containingFunction = containingNonArrowFunction = member; - const statement = createStatement( + /** + * 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( - getClassMemberPrefix(node, member), + receiver, visitNode(member.name, visitor, isPropertyName) ), - transformFunctionLikeToExpression(member) + transformFunctionLikeToExpression(member, /*location*/ undefined, /*name*/ undefined) ), /*location*/ member ); - - containingFunction = savedContainingFunction; - containingNonArrowFunction = savedContainingNonArrowFunction; - return statement; } + /** + * 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 { - const savedContainingFunction = containingFunction; - const savedContainingNonArrowFunction = containingNonArrowFunction; - containingFunction = containingNonArrowFunction = accessors.firstAccessor; - const statement = createStatement( + return createStatement( transformAccessorsToExpression(receiver, accessors) ); - containingFunction = savedContainingFunction; - containingNonArrowFunction = savedContainingNonArrowFunction; - return statement; } + /** + * 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, @@ -570,8 +768,8 @@ namespace ts { /*location*/ firstAccessor.name ), { - get: getAccessor && transformFunctionLikeToExpression(getAccessor, /*location*/ getAccessor), - set: setAccessor && transformFunctionLikeToExpression(setAccessor, /*location*/ setAccessor), + get: getAccessor && transformFunctionLikeToExpression(getAccessor, /*location*/ getAccessor, /*name*/ undefined), + set: setAccessor && transformFunctionLikeToExpression(setAccessor, /*location*/ setAccessor, /*name*/ undefined), enumerable: true, configurable: true }, @@ -580,175 +778,289 @@ namespace ts { ); } - function transformFunctionLikeToExpression(node: FunctionLikeDeclaration, location?: TextRange, name?: Identifier): FunctionExpression { - return setOriginalNode( - createFunctionExpression( - /*asteriskToken*/ undefined, - name, - visitNodes(node.parameters, visitor, isParameter), - transformFunctionBody(node), - location - ), - node - ); - } - + /** + * Visits an ArrowFunction and transforms it into a FunctionExpression. + * + * @param node An ArrowFunction node. + */ function visitArrowFunction(node: ArrowFunction) { if (node.transformFlags & TransformFlags.ContainsLexicalThis) { - enableExpressionSubstitutionForCapturedThis(); + enableSubstitutionsForCapturedThis(); } return transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined); } + /** + * 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 setOriginalNode( - createFunctionDeclaration( - /*modifiers*/ undefined, - node.asteriskToken, // TODO(rbuckton): downlevel support for generators - node.name, - visitNodes(node.parameters, visitor, isParameter), - transformFunctionBody(node), - /*location*/ node - ), - node + 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); - addDefaultValueAssignments(statements, node); - addRestParameter(statements, node); + addDefaultValueAssignmentsIfNeeded(statements, node); + addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false); const body = node.body; if (isBlock(body)) { - addNodes(statements, visitNodes(body.statements, visitor, isStatement)); + addRange(statements, visitNodes(body.statements, visitor, isStatement)); } else { const expression = visitNode(body, visitor, isExpression); if (expression) { - addNode(statements, createReturn(expression, /*location*/ body)); + statements.push(createReturn(expression, /*location*/ body)); } } - addNodes(statements, endLexicalEnvironment()); + addRange(statements, endLexicalEnvironment()); return createBlock(statements, node.body); } - function visitBinaryExpression(node: BinaryExpression): Expression { + /** + * 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. - // TODO(rbuckton): Determine whether we need to save the value. - return flattenDestructuringAssignment(node, /*needsValue*/ true, hoistVariableDeclaration, visitor); + 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( - flattenNodes(map(node.declarations, visitVariableDeclaration)), + flattenNodes(map(node.declarations, visitVariableDeclarationInLetDeclarationList)), /*location*/ node ), node ); } - function visitVariableDeclaration(node: VariableDeclaration): OneOrMany { + /** + * 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 createNodeArrayNode( - flattenVariableDestructuring(node, /*value*/ undefined, visitor) - ); + if (isBindingPattern(name) || node.initializer) { + return visitVariableDeclaration(node); } - else { - let initializer = node.initializer; - // For binding pattern names that lack initializer 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 - if (!initializer) { - const original = getOriginalNode(node); - if (isVariableDeclaration(original)) { - // 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 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 emittedAsNestedLetDeclaration = combinedNodeFlags & NodeFlags.Let && !emittedAsTopLevel; - - const emitExplicitInitializer = - emittedAsNestedLetDeclaration - && enclosingBlockScopeContainer.kind !== SyntaxKind.ForInStatement - && enclosingBlockScopeContainer.kind !== SyntaxKind.ForOfStatement - && (!resolver.isDeclarationWithCollidingName(original) - || (isDeclaredInLoop - && !isCapturedInFunction - && !isIterationStatement(enclosingBlockScopeContainer, /*lookInLabeledStatements*/ false))); - - if (emitExplicitInitializer) { - initializer = createVoidZero(); - } - } - } - return setOriginalNode( - createVariableDeclaration( - name, - visitNode(initializer, visitor, isExpression, /*optional*/ true), - /*location*/ node - ), - node - ); + if (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): OneOrMany { + // If we are here it is because the name contains a binding pattern. + Debug.assert(isBindingPattern(node.name)); + + return createNodeArrayNode( + flattenVariableDestructuring(node, /*value*/ undefined, visitor) + ); } function visitLabeledStatement(node: LabeledStatement) { @@ -771,12 +1083,16 @@ namespace ts { 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. @@ -803,7 +1119,7 @@ namespace ts { const expression = visitNode(node.expression, visitor, isExpression); const initializer = node.initializer; - const loopBodyStatements: Statement[] = []; + const statements: Statement[] = []; // In the case where the user wrote an identifier as the RHS, like this: // @@ -822,7 +1138,7 @@ namespace ts { 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. - addNode(loopBodyStatements, + statements.push( createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList( @@ -839,7 +1155,7 @@ namespace ts { else { // The following call does not include the initializer, so we have // to emit it separately. - addNode(loopBodyStatements, + statements.push( createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList([ @@ -859,7 +1175,7 @@ namespace ts { const assignment = createAssignment(initializer, createElementAccess(rhsReference, counter)); if (isDestructuringAssignment(assignment)) { // This is a destructuring pattern, so we flatten the destructuring instead. - addNode(loopBodyStatements, + statements.push( createStatement( flattenDestructuringAssignment( assignment, @@ -871,16 +1187,16 @@ namespace ts { ); } else { - addNode(loopBodyStatements, createStatement(assignment, /*location*/ node.initializer)); + statements.push(createStatement(assignment, /*location*/ node.initializer)); } } const statement = visitNode(node.statement, visitor, isStatement); if (isBlock(statement)) { - addNodes(loopBodyStatements, statement.statements); + addRange(statements, statement.statements); } else { - addNode(loopBodyStatements, statement); + statements.push(statement); } return createFor( @@ -898,12 +1214,17 @@ namespace ts { ), createPostfixIncrement(counter, /*location*/ initializer), createBlock( - loopBodyStatements + 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; @@ -931,10 +1252,9 @@ namespace ts { addNode(expressions, createAssignment( temp, - setMultiLine( - createObjectLiteral( - visitNodes(properties, visitor, isObjectLiteralElement, 0, numInitialNonComputedProperties) - ), + createObjectLiteral( + visitNodes(properties, visitor, isObjectLiteralElement, 0, numInitialNonComputedProperties), + /*location*/ undefined, node.multiLine ) ), @@ -945,13 +1265,23 @@ namespace ts { // We need to clone the temporary identifier so that we can write it on a // new line - addNode(expressions, cloneNode(temp), node.multiLine); + 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; - for (let i = numInitialNonComputedProperties, len = properties.length; i < len; i++) { + const numProperties = properties.length; + for (let i = numInitialNonComputedProperties; i < numProperties; i++) { const property = properties[i]; switch (property.kind) { case SyntaxKind.GetAccessor: @@ -982,6 +1312,13 @@ namespace ts { } } + /** + * 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( @@ -993,6 +1330,13 @@ namespace ts { ); } + /** + * 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( @@ -1004,29 +1348,47 @@ namespace ts { ); } + /** + * 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), + 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(isIdentifier(node.name), `Unexpected node kind: ${formatSyntaxKind(node.name.kind)}.`); + Debug.assert(!isComputedPropertyName(node.name)); return createPropertyAssignment( node.name, - transformFunctionLikeToExpression(node, /*location*/ node), + 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, @@ -1035,197 +1397,148 @@ namespace ts { ); } + /** + * Visits an ArrayLiteralExpression that contains a spread element. + * + * @param node An ArrayLiteralExpression node. + */ function visitArrayLiteralExpression(node: ArrayLiteralExpression): Expression { - // We are here either because SuperKeyword was used somewhere in the expression, or - // because we contain a SpreadElementExpression. - if (node.transformFlags & TransformFlags.ContainsSpreadElementExpression) { - return transformAndSpreadElements(node.elements, /*needsUniqueCopy*/ true, node.multiLine); - } - else { - // We don't handle SuperKeyword here, so fall back. - return visitEachChild(node, visitor, context); - } + // 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 } = transformCallTarget(node.expression); + + const { target, thisArg } = createCallBinding(node); 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( - target, - thisArg, + visitNode(target, visitor, isExpression), + visitNode(thisArg, visitor, isExpression), transformAndSpreadElements(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false) ); } else { - Debug.assert(isSuperCall(node)); + // [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( - target, - thisArg, + 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 either because we contain a SpreadElementExpression. + // We are here because we contain a SpreadElementExpression. Debug.assert((node.transformFlags & TransformFlags.ContainsSpreadElementExpression) !== 0); - // Transforms `new C(...a)` into `new ((_a = C).bind.apply(_a, [void 0].concat(a)))()`. - // Transforms `new x.C(...a)` into `new ((_a = x.C).bind.apply(_a, [void 0].concat(a)))()`. - const { target, thisArg } = transformCallTarget(createPropertyAccess(node.expression, "bind")); + // [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( - createParen( - createFunctionApply( - target, - thisArg, - transformAndSpreadElements(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false, createVoidZero()) - ) + createFunctionApply( + visitNode(target, visitor, isExpression), + thisArg, + transformAndSpreadElements(createNodeArray([createVoidZero(), ...node.arguments]), /*needsUniqueCopy*/ false, /*multiLine*/ false) ), [] ); } - interface CallTarget { - target: Expression; - thisArg: Expression; - } - - function transformCallTarget(expression: Expression): CallTarget { - const callee = skipParentheses(expression); - switch (callee.kind) { - case SyntaxKind.PropertyAccessExpression: - return transformPropertyAccessCallTarget(callee); - - case SyntaxKind.ElementAccessExpression: - return transformElementAccessCallTarget(callee); - - case SyntaxKind.SuperKeyword: - return transformSuperCallTarget(callee); - - default: - const thisArg = createVoidZero(); - const target = visitNode(expression, visitor, isExpression); - return { target, thisArg }; - } - } - - function transformPropertyAccessCallTarget(node: PropertyAccessExpression): CallTarget { - if (node.expression.kind === SyntaxKind.SuperKeyword) { - // For `super.b()`, target is either `_super.b` (for static members) or - // `_super.prototype.b` (for instance members), and thisArg is `this`. - const thisArg = createThis(/*location*/ node.expression); - const target = createPropertyAccess( - visitSuperKeyword(node.expression), - node.name - ); - - return { target, thisArg }; - } - else { - // For `a.b()`, target is `(_a = a).b` and thisArg is `_a`. - const thisArg = createTempVariable(); - const target = createPropertyAccess( - createAssignment( - thisArg, - visitNode(node.expression, visitor, isExpression) - ), - node.name - ); - - return { target, thisArg }; - } - } - - function transformElementAccessCallTarget(node: ElementAccessExpression): CallTarget { - if (node.expression.kind === SyntaxKind.SuperKeyword) { - // For `super[b]()`, target is either `_super[b]` (for static members) or - // `_super.prototype[b]` (for instance members), and thisArg is `this`. - const thisArg = createThis(/*location*/ node.expression); - const target = createElementAccess( - visitSuperKeyword(node.expression), - visitNode(node.argumentExpression, visitor, isExpression) - ); - - return { target, thisArg }; - } - else { - // For `a[b]()`, expression is `(_a = a)[b]` and thisArg is `_a`. - const thisArg = createTempVariable(); - const target = createElementAccess( - createAssignment( - thisArg, - visitNode(node.expression, visitor, isExpression) - ), - visitNode(node.argumentExpression, visitor, isExpression) - ); - - return { target, thisArg }; - } - } - - function transformSuperCallTarget(node: PrimaryExpression): CallTarget { - // For `super()`, expression is `_super` and thisArg is `this`. - const thisArg = createThis(/*location*/ node); - const target = createIdentifier("_super"); - return { target, thisArg }; - } - - function transformAndSpreadElements(elements: NodeArray, needsUniqueCopy: boolean, multiLine: boolean, leadingExpression?: Expression): Expression { - const segments: Expression[] = []; - addNode(segments, leadingExpression); - - const length = elements.length; - let start = 0; - for (let i = 0; i < length; i++) { - const element = elements[i]; - if (isSpreadElementExpression(element)) { - if (i > start) { - addNode(segments, - setMultiLine( - createArrayLiteral( - visitNodes(elements, visitor, isExpression, start, i) - ), - multiLine - ) - ); - } - - addNode(segments, visitNode(element.expression, visitor, isExpression)); - start = i + 1; - } - } + /** + * 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]) - if (start < length) { - addNode(segments, - setMultiLine( - createArrayLiteral( - visitNodes(elements, visitor, isExpression, start, length) - ), - multiLine - ) - ); - } + // 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) { - if (!leadingExpression && needsUniqueCopy && isSpreadElementExpression(elements[0])) { - return createArraySlice(segments[0]); - } - - return segments[0]; + 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); } - function visitTaggedTemplateExpression(node: TaggedTemplateExpression): LeftHandSideExpression { + /** + * Visits a TaggedTemplateExpression node. + * + * @param node A TaggedTemplateExpression node. + */ + function visitTaggedTemplateExpression(node: TaggedTemplateExpression) { // Visit the tag expression const tag = visitNode(node.tag, visitor, isExpression); @@ -1239,31 +1552,31 @@ namespace ts { const rawStrings: Expression[] = []; const template = node.template; if (isNoSubstitutionTemplateLiteral(template)) { - addNode(cookedStrings, createLiteral(template.text)); - addNode(rawStrings, getRawLiteral(template)); + cookedStrings.push(createLiteral(template.text)); + rawStrings.push(getRawLiteral(template)); } else { - addNode(cookedStrings, createLiteral(template.head.text)); - addNode(rawStrings, getRawLiteral(template.head)); + cookedStrings.push(createLiteral(template.head.text)); + rawStrings.push(getRawLiteral(template.head)); for (const templateSpan of template.templateSpans) { - addNode(cookedStrings, createLiteral(templateSpan.literal.text)); - addNode(rawStrings, getRawLiteral(templateSpan.literal)); - addNode(templateArguments, visitNode(templateSpan.expression, visitor, isExpression)); + cookedStrings.push(createLiteral(templateSpan.literal.text)); + rawStrings.push(getRawLiteral(templateSpan.literal)); + templateArguments.push(visitNode(templateSpan.expression, visitor, isExpression)); } } - return createParen( - inlineExpressions([ - createAssignment(temp, createArrayLiteral(cookedStrings)), - createAssignment(createPropertyAccess(temp, "raw"), createArrayLiteral(rawStrings)), - createCall( - tag, - templateArguments - ) - ]) - ); + 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. @@ -1285,10 +1598,15 @@ namespace ts { return createLiteral(text); } + /** + * Visits a TemplateExpression node. + * + * @param node A TemplateExpression node. + */ function visitTemplateExpression(node: TemplateExpression): Expression { const expressions: Expression[] = []; addTemplateHead(expressions, node); - addTemplateSpans(expressions, node.templateSpans); + 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 @@ -1307,6 +1625,11 @@ namespace ts { 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. @@ -1329,28 +1652,43 @@ namespace ts { 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; } - addNode(expressions, createLiteral(node.head.text)); + expressions.push(createLiteral(node.head.text)); } - function addTemplateSpans(expressions: Expression[], nodes: TemplateSpan[]): void { - for (const node of nodes) { - addNode(expressions, visitNode(node.expression, visitor, isExpression)); + /** + * 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 (node.literal.text.length !== 0) { - addNode(expressions, createLiteral(node.literal.text)); + 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) @@ -1360,89 +1698,120 @@ namespace ts { } function visitSourceFileNode(node: SourceFile): SourceFile { - const clone = cloneNode(node, node, node.flags, /*parent*/ undefined, node); + const [prologue, remaining] = span(node.statements, isPrologueDirective); const statements: Statement[] = []; startLexicalEnvironment(); - const statementOffset = addPrologueDirectives(statements, node.statements); + addRange(statements, prologue); addCaptureThisForNodeIfNeeded(statements, node); - addNodes(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); - addNodes(statements, endLexicalEnvironment()); - clone.statements = createNodeArray(statements, node.statements); + addRange(statements, visitNodes(createNodeArray(remaining), visitor, isStatement)); + addRange(statements, endLexicalEnvironment()); + const clone = cloneNode(node, node, node.flags, /*parent*/ undefined, node); + clone.statements = createNodeArray(statements, /*location*/ node.statements); return clone; } - function addPrologueDirectives(to: Statement[], from: NodeArray): number { - for (let i = 0; i < from.length; i++) { - if (isPrologueDirective(from[i])) { - addNode(to, from[i]); - } - else { - return i; - } - } - - return from.length; - } - + /** + * Called by the printer just before a node is printed. + * + * @param node The node to be printed. + */ function onBeforeEmitNode(node: Node) { previousOnBeforeEmitNode(node); - if (containingFunctionStack && isFunctionLike(node)) { - containingFunctionStack.push(node); + 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. + pushContainingFunction(node.kind === SyntaxKind.ArrowFunction); } } + /** + * Called by the printer just after a node is printed. + * + * @param node The node that was printed. + */ function onAfterEmitNode(node: Node) { previousOnAfterEmitNode(node); - if (containingFunctionStack && isFunctionLike(node)) { - containingFunctionStack.pop(); + if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && isFunctionLike(node)) { + // If we are tracking a captured `this`, pop the last containing function bit. + popContainingFunction(); + } + } + + /** + * 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); } } - function enableExpressionSubstitutionForCapturedThis() { - if (!hasEnabledExpressionSubstitutionForCapturedThis) { - hasEnabledExpressionSubstitutionForCapturedThis = true; - enableExpressionSubstitution(SyntaxKind.ThisKeyword); - enableEmitNotification(SyntaxKind.Constructor); - enableEmitNotification(SyntaxKind.MethodDeclaration); - enableEmitNotification(SyntaxKind.GetAccessor); - enableEmitNotification(SyntaxKind.SetAccessor); - enableEmitNotification(SyntaxKind.ArrowFunction); - enableEmitNotification(SyntaxKind.FunctionExpression); - enableEmitNotification(SyntaxKind.FunctionDeclaration); - containingFunctionStack = []; + /** + * 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; + containingFunctionDepth = 0; + 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); - const original = getOriginalNode(node); - if (isIdentifier(original) && isNameOfDeclarationWithCollidingName(original)) { - return getGeneratedNameForNode(original); + // 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; - if (parent) { - switch (parent.kind) { - case SyntaxKind.BindingElement: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.VariableDeclaration: - return (parent).name === node - && resolver.isDeclarationWithCollidingName(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) { @@ -1456,29 +1825,93 @@ namespace ts { return node; } + /** + * Substitutes an expression identifier. + * + * @param node An Identifier node. + */ function substituteExpressionIdentifier(node: Identifier): Identifier { - const original = getOriginalNode(node); - if (isIdentifier(original)) { - const declaration = resolver.getReferencedDeclarationWithCollidingName(original); - if (declaration) { - return getGeneratedNameForNode(declaration.name); + 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 (containingFunctionStack) { - const containingFunction = lastOrUndefined(containingFunctionStack); - if (containingFunction && getOriginalNode(containingFunction).kind === SyntaxKind.ArrowFunction) { - return createIdentifier("_this"); - } + if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && isContainedInArrowFunction()) { + return createIdentifier("_this", /*location*/ node); } return node; } + /** + * Pushes a value onto a stack that indicates whether we are currently printing a node + * within an arrow function. This is used to determine whether we need to capture `this`. + * + * @param isArrowFunction A value indicating whether the current function container is + * an arrow function. + */ + function pushContainingFunction(isArrowFunction: boolean) { + // Encode whether the containing function is an arrow function in the first 31 bits of + // an integer. If the stack grows beyond a depth of 31 functions, use an array. + if (containingFunctionDepth > 0 && containingFunctionDepth % 31 === 0) { + if (!containingFunctionStack) { + containingFunctionStack = [containingFunctionState]; + } + else { + containingFunctionStack.push(containingFunctionState); + } + + containingFunctionState = 0; + } + + if (isArrowFunction) { + containingFunctionState |= 1 << (containingFunctionDepth % 31); + } + + containingFunctionDepth++; + } + + /** + * Pops a value off of the containing function stack. + */ + function popContainingFunction() { + if (containingFunctionDepth > 0) { + containingFunctionDepth--; + if (containingFunctionDepth === 0) { + containingFunctionState = 0; + } + else if (containingFunctionDepth % 31 === 0) { + containingFunctionState = containingFunctionStack.pop(); + } + else { + containingFunctionState &= ~(1 << containingFunctionDepth % 31); + } + } + } + + /** + * Gets a value indicating whether we are currently printing a node inside of an arrow + * function. + */ + function isContainedInArrowFunction() { + return containingFunctionDepth > 0 + && containingFunctionState & (1 << (containingFunctionDepth - 1) % 31); + } + function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) { return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node); } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 84bfb04976d2d..a32b13a2551bc 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -44,7 +44,6 @@ namespace ts { let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; let currentParent: Node; let currentNode: Node; - let combinedNodeFlags: NodeFlags; /** * Keeps track of whether expression substitution has been enabled for specific edge cases. @@ -104,7 +103,6 @@ namespace ts { const savedCurrentScope = currentScope; const savedCurrentParent = currentParent; const savedCurrentNode = currentNode; - const savedCombinedNodeFlags = combinedNodeFlags; // Handle state changes before visiting a node. onBeforeVisitNode(node); @@ -116,7 +114,6 @@ namespace ts { currentScope = savedCurrentScope; currentParent = savedCurrentParent; currentNode = savedCurrentNode; - combinedNodeFlags = savedCombinedNodeFlags; return node; } @@ -392,8 +389,6 @@ namespace ts { currentParent = currentNode; currentNode = node; - combinedNodeFlags = combineNodeFlags(currentNode, currentParent, combinedNodeFlags); - switch (node.kind) { case SyntaxKind.SourceFile: case SyntaxKind.CaseBlock: @@ -2758,7 +2753,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/types.ts b/src/compiler/types.ts index 5cced242498b2..da0f983298f70 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2761,18 +2761,19 @@ 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, // Assertions // - Bitmasks that are used to assert facts about the syntax of a node and its subtree. @@ -2784,7 +2785,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, ArrowFunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding, FunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding, ConstructorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 11aa14cf5b855..9568a8635032d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -491,19 +491,6 @@ namespace ts { return node; } - /** - * Combines the flags of a node with the combined flags of its parent if they can be combined. - */ - export function combineNodeFlags(node: Node, parentNode: Node, previousNodeFlags: NodeFlags) { - if ((node.kind === SyntaxKind.VariableDeclarationList && parentNode.kind === SyntaxKind.VariableStatement) || - (node.kind === SyntaxKind.VariableDeclaration && parentNode.kind === SyntaxKind.VariableDeclarationList) || - (node.kind === SyntaxKind.BindingElement)) { - return node.flags | previousNodeFlags; - } - - return node.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 @@ -948,19 +935,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 { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 40568b6fa635a..5bbf82865e6eb 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -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 }, ], @@ -630,7 +630,7 @@ namespace ts { if (updated !== undefined || visited !== value) { if (updated === undefined) { - updated = getMutableNode(node); + updated = getMutableClone(node); updated.flags &= ~NodeFlags.Modifier; } From 2d2709f8a5a3923d74dd24084c65b87e777f8e8d Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 2 Mar 2016 12:05:28 -0800 Subject: [PATCH 29/43] Fixed typo in visitCallExpression --- src/compiler/printer.ts | 14 ++++++++++---- src/compiler/transformers/es6.ts | 6 +++--- src/compiler/types.ts | 1 + 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 331e502b9315b..0242437c61536 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -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; @@ -209,6 +210,7 @@ const _super = (function (geti, seti) { startLexicalEnvironment = undefined; endLexicalEnvironment = undefined; getNodeEmitFlags = undefined; + setNodeEmitFlags = undefined; isExpressionSubstitutionEnabled = undefined; isEmitNotificationEnabled = undefined; expressionSubstitution = undefined; @@ -230,6 +232,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; @@ -1968,10 +1971,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); + emitWorker(substitute); + return true; + } } return false; diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 4db823266c9fd..11337d07264e4 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1036,11 +1036,11 @@ namespace ts { // 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) || node.initializer) { + if (isBindingPattern(name)) { return visitVariableDeclaration(node); } - if (shouldEmitExplicitInitializerForLetDeclaration(node)) { + if (!node.initializer && shouldEmitExplicitInitializerForLetDeclaration(node)) { const clone = getMutableClone(node); clone.initializer = createVoidZero(); return clone; @@ -1416,7 +1416,7 @@ namespace ts { // We are here either because SuperKeyword was used somewhere in the expression, or // because we contain a SpreadElementExpression. - const { target, thisArg } = createCallBinding(node); + const { target, thisArg } = createCallBinding(node.expression); if (node.transformFlags & TransformFlags.ContainsSpreadElementExpression) { // [source] // f(...a, b) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index da0f983298f70..516e90af2ddef 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2809,6 +2809,7 @@ 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. } /** Additional context provided to `visitEachChild` */ From ebb47649ee2f12b7e37a46c73ede2c2ffc6e69a2 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 2 Mar 2016 15:19:42 -0800 Subject: [PATCH 30/43] Switch from onBefore/onAfterEmitNode to onEmitNode --- src/compiler/printer.ts | 78 ++++++++++++++++++++++++------------- src/compiler/transformer.ts | 3 +- src/compiler/types.ts | 12 ++---- 3 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index e44e88b46128d..04ab37521801d 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -151,8 +151,7 @@ const _super = (function (geti, seti) { let isEmitNotificationEnabled: (node: Node) => boolean; let expressionSubstitution: (node: Expression) => Expression; let identifierSubstitution: (node: Identifier) => Identifier; - let onBeforeEmitNode: (node: Node) => void; - let onAfterEmitNode: (node: Node) => void; + let onEmitNode: (node: Node, emit: (node: Node) => void) => void; let nodeToGeneratedName: string[]; let generatedNameSet: Map; let tempFlags: TempFlags; @@ -213,8 +212,7 @@ const _super = (function (geti, seti) { isEmitNotificationEnabled = undefined; expressionSubstitution = undefined; identifierSubstitution = undefined; - onBeforeEmitNode = undefined; - onAfterEmitNode = undefined; + onEmitNode = undefined; tempFlags = TempFlags.Auto; currentSourceFile = undefined; currentText = undefined; @@ -234,8 +232,7 @@ const _super = (function (geti, seti) { isEmitNotificationEnabled = context.isEmitNotificationEnabled; expressionSubstitution = context.expressionSubstitution; identifierSubstitution = context.identifierSubstitution; - onBeforeEmitNode = context.onBeforeEmitNode; - onAfterEmitNode = context.onAfterEmitNode; + onEmitNode = context.onEmitNode; return printSourceFile; } @@ -249,21 +246,52 @@ const _super = (function (geti, seti) { return node; } + /** + * Emits a node. + */ function emit(node: Node) { - emitWithWorker(node, emitWorker); + emitNodeWithNotificationOption(node, emitWithoutNotificationOption); + } + + /** + * Emits a node without calling onEmitNode. + * NOTE: Do not call this method directly. + */ + function emitWithoutNotificationOption(node: Node) { + emitNodeWithWorker(node, emitWorker); } + /** + * Emits an expression node. + */ function emitExpression(node: Expression) { - emitWithWorker(node, emitExpressionWorker); + emitNodeWithNotificationOption(node, emitExpressionWithoutNotificationOption); + } + + /** + * Emits an expression without calling onEmitNode. + * NOTE: Do not call this method directly. + */ + function emitExpressionWithoutNotificationOption(node: Expression) { + emitNodeWithWorker(node, emitExpressionWorker); } - function emitWithWorker(node: Node, emitWorker: (node: Node) => void) { + /** + * Emits a node with emit notification if available. + */ + function emitNodeWithNotificationOption(node: Node, emit: (node: Node) => void) { if (node) { - const adviseOnEmit = isEmitNotificationEnabled(node); - if (adviseOnEmit && onBeforeEmitNode) { - onBeforeEmitNode(node); + if (isEmitNotificationEnabled(node)) { + onEmitNode(node, emit); + } + else { + emit(node); } + } + } + function emitNodeWithWorker(node: Node, emitWorker: (node: Node) => void) { + if (node) { const leadingComments = getLeadingComments(node, getNotEmittedParent); const trailingComments = getTrailingComments(node, getNotEmittedParent); emitLeadingComments(node, leadingComments); @@ -271,24 +299,9 @@ const _super = (function (geti, seti) { emitWorker(node); emitEnd(node); emitTrailingComments(node, trailingComments); - - if (adviseOnEmit && onAfterEmitNode) { - onAfterEmitNode(node); - } } } - function getNotEmittedParent(node: Node): Node { - if (getNodeEmitFlags(node) & NodeEmitFlags.EmitCommentsOfNotEmittedParent) { - const parent = getOriginalNode(node).parent; - if (getNodeEmitFlags(parent) & NodeEmitFlags.IsNotEmittedNode) { - return parent; - } - } - - return undefined; - } - function emitWorker(node: Node): void { const kind = node.kind; switch (kind) { @@ -2361,6 +2374,17 @@ const _super = (function (geti, seti) { && rangeEndIsOnSameLineAsRangeStart(block, block); } + function getNotEmittedParent(node: Node): Node { + if (getNodeEmitFlags(node) & NodeEmitFlags.EmitCommentsOfNotEmittedParent) { + const parent = getOriginalNode(node).parent; + if (getNodeEmitFlags(parent) & NodeEmitFlags.IsNotEmittedNode) { + return parent; + } + } + + return undefined; + } + function isUniqueName(name: string): boolean { return !resolver.hasGlobalName(name) && !hasProperty(currentFileIdentifiers, name) && diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 98b71f0c16e40..e2d309b00a182 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -46,8 +46,7 @@ namespace ts { expressionSubstitution: node => node, enableExpressionSubstitution, isExpressionSubstitutionEnabled, - onBeforeEmitNode: node => { }, - onAfterEmitNode: node => { }, + onEmitNode: (node, emit) => emit(node), enableEmitNotification, isEmitNotificationEnabled, }; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 07d1daec6699c..12778d81439f0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2865,16 +2865,10 @@ namespace ts { isEmitNotificationEnabled(node: Node): boolean; /** - * Hook used to notify transformers immediately before the pretty printer - * emits a node. + * Hook used to allow transformers to capture state before or after + * the printer emits a node. */ - onBeforeEmitNode?: (node: Node) => void; - - /** - * Hook used to notify transformers immediately after the pretty printer - * emits a node. - */ - onAfterEmitNode?: (node: Node) => void; + onEmitNode?: (node: Node, emit: (node: Node) => void) => void; } /* @internal */ From 8b35af4ce5246c045f0a413602665ea51411991d Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 2 Mar 2016 15:39:56 -0800 Subject: [PATCH 31/43] Switched to onEmitNode --- src/compiler/transformers/ts.ts | 140 ++++++++++++++------------------ 1 file changed, 61 insertions(+), 79 deletions(-) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 58f06cb214e3d..990c3e66c666c 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -28,13 +28,11 @@ namespace ts { const languageVersion = getEmitScriptTarget(compilerOptions); // Save the previous transformation hooks. - const previousOnBeforeEmitNode = context.onBeforeEmitNode; - const previousOnAfterEmitNode = context.onAfterEmitNode; + const previousOnEmitNode = context.onEmitNode; const previousExpressionSubstitution = context.expressionSubstitution; // Set new transformation hooks. - context.onBeforeEmitNode = onBeforeEmitNode; - context.onAfterEmitNode = onAfterEmitNode; + context.onEmitNode = onEmitNode; context.expressionSubstitution = substituteExpression; // These variables contain state that changes as we descend into the tree. @@ -64,19 +62,16 @@ namespace ts { let currentDecoratedClassAliases: Map; /** - * Keeps track of how deeply nested we are within any containing namespaces - * when performing just-in-time substitution while printing an expression identifier. - * If the nest level is greater than zero, then we are performing a substitution - * inside of a namespace and we should perform the more costly checks to determine - * whether the identifier points to an exported declaration. + * Keeps track of whether we are within any containing namespaces when performing + * just-in-time substitution while printing an expression identifier. */ - let namespaceNestLevel: number; + let isEnclosedInNamespace: boolean; /** - * This array keeps track of containers where `super` is valid, for use with + * This keeps track of containers where `super` is valid, for use with * just-in-time substitution for `super` expressions inside of async methods. */ - let superContainerStack: SuperContainer[]; + let currentSuperContainer: SuperContainer; return transformSourceFile; @@ -2378,21 +2373,24 @@ namespace ts { // x_1.y = ...; // })(x || (x = {})); statements.push( - setOriginalNode( - createStatement( - createCall( - createParen( - createFunctionExpression( - /*asteriskToken*/ undefined, - /*name*/ undefined, - [createParameter(currentNamespaceLocalName)], - transformModuleBody(node) - ) - ), - [moduleParam] - ) + setNodeEmitFlags( + setOriginalNode( + createStatement( + createCall( + createParen( + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + [createParameter(currentNamespaceLocalName)], + transformModuleBody(node) + ) + ), + [moduleParam] + ) + ), + node ), - node + NodeEmitFlags.AdviseOnEmitNode ) ); @@ -2594,62 +2592,51 @@ namespace ts { : getClassPrototype(node); } - function onBeforeEmitNode(node: Node): void { - previousOnBeforeEmitNode(node); + function isClassWithDecorators(node: Node): node is ClassDeclaration { + return node.kind === SyntaxKind.ClassDeclaration && node.decorators !== undefined; + } + function isSuperContainer(node: Node): node is SuperContainer { const kind = node.kind; - if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses - && kind === SyntaxKind.ClassDeclaration - && node.decorators) { - currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)]; - } + return kind === SyntaxKind.ClassDeclaration + || kind === SyntaxKind.Constructor + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor; + } - if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports - && (kind === SyntaxKind.ClassDeclaration - || kind === SyntaxKind.Constructor - || kind === SyntaxKind.MethodDeclaration - || kind === SyntaxKind.GetAccessor - || kind === SyntaxKind.SetAccessor)) { + function isTransformedModuleDeclaration(node: Node): boolean { + return getOriginalNode(node).kind === SyntaxKind.ModuleDeclaration; + } - if (!superContainerStack) { - superContainerStack = []; - } + function onEmitNode(node: Node, emit: (node: Node) => void): void { + const savedIsEnclosedInNamespace = isEnclosedInNamespace; + const savedCurrentSuperContainer = currentSuperContainer; - superContainerStack.push(node); + // If we need support substitutions for aliases for decorated classes, + // we should enable it here. + if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses && isClassWithDecorators(node)) { + currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)]; } - if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports - && kind === SyntaxKind.ModuleDeclaration) { - namespaceNestLevel++; + // If we need to support substitutions for `super` in an async method, + // we should track it here. + if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) { + currentSuperContainer = node; } - } - function onAfterEmitNode(node: Node): void { - previousOnAfterEmitNode(node); - - const kind = node.kind; - if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses - && kind === SyntaxKind.ClassDeclaration - && node.decorators) { - currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined; + if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) { + isEnclosedInNamespace = true; } - if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports - && (kind === SyntaxKind.ClassDeclaration - || kind === SyntaxKind.Constructor - || kind === SyntaxKind.MethodDeclaration - || kind === SyntaxKind.GetAccessor - || kind === SyntaxKind.SetAccessor)) { + previousOnEmitNode(node, emit); - if (superContainerStack) { - superContainerStack.pop(); - } + if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses && isClassWithDecorators(node)) { + currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined; } - if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports - && kind === SyntaxKind.ModuleDeclaration) { - namespaceNestLevel--; - } + isEnclosedInNamespace = savedIsEnclosedInNamespace; + currentSuperContainer = savedCurrentSuperContainer; } function substituteExpression(node: Expression): Expression { @@ -2660,7 +2647,7 @@ namespace ts { return substituteExpressionIdentifier(node); } - if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) { + if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper) { switch (node.kind) { case SyntaxKind.CallExpression: return substituteCallExpression(node); @@ -2691,8 +2678,7 @@ namespace ts { } } - if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports - && namespaceNestLevel > 0) { + if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isEnclosedInNamespace) { // If we are nested within a namespace declaration, we may need to qualifiy // an identifier that is exported from a merged namespace. const original = getOriginalNode(node); @@ -2758,8 +2744,8 @@ namespace ts { } function enableExpressionSubstitutionForAsyncMethodsWithSuper() { - if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) === 0) { - enabledSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports; + if ((enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper) === 0) { + enabledSubstitutions |= TypeScriptSubstitutionFlags.AsyncMethodsWithSuper; // We need to enable substitutions for call, property access, and element access // if we need to rewrite super calls. @@ -2800,9 +2786,6 @@ namespace ts { // We need to be notified when entering and exiting namespaces. context.enableEmitNotification(SyntaxKind.ModuleDeclaration); - - // Keep track of namespace nesting depth - namespaceNestLevel = 0; } } @@ -2827,9 +2810,8 @@ namespace ts { } function getSuperContainerAsyncMethodFlags() { - const container = lastOrUndefined(superContainerStack); - return container !== undefined - && resolver.getNodeCheckFlags(getOriginalNode(container)) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding); + return currentSuperContainer !== undefined + && resolver.getNodeCheckFlags(getOriginalNode(currentSuperContainer)) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding); } } } \ No newline at end of file From 1c738181efcd1e84054e78ea7494a9f30aef429e Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 2 Mar 2016 15:47:28 -0800 Subject: [PATCH 32/43] Switched to onEmitNode --- src/compiler/transformers/es6.ts | 102 +++---------------------------- 1 file changed, 10 insertions(+), 92 deletions(-) diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 11337d07264e4..ebc50ad7b1676 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -22,10 +22,8 @@ namespace ts { const resolver = context.getEmitResolver(); const previousIdentifierSubstitution = context.identifierSubstitution; const previousExpressionSubstitution = context.expressionSubstitution; - const previousOnBeforeEmitNode = context.onBeforeEmitNode; - const previousOnAfterEmitNode = context.onAfterEmitNode; - context.onBeforeEmitNode = onBeforeEmitNode; - context.onAfterEmitNode = onAfterEmitNode; + const previousOnEmitNode = context.onEmitNode; + context.onEmitNode = onEmitNode; context.identifierSubstitution = substituteIdentifier; context.expressionSubstitution = substituteExpression; @@ -44,23 +42,9 @@ namespace ts { let enabledSubstitutions: ES6SubstitutionFlags; /** - * Keeps track of how deeply nested we are within function-likes when printing - * nodes. This is used to determine whether we need to emit `_this` instead of - * `this`. + * This is used to determine whether we need to emit `_this` instead of `this`. */ - let containingFunctionDepth: number; - - /** - * The first 31 bits are used to determine whether a containing function is an - * arrow function. - */ - let containingFunctionState: number; - - /** - * If the containingFunctionDepth grows beyond 31 nested function-likes, this - * array is used as a stack to track deeper levels of nesting. - */ - let containingFunctionStack: number[]; + let useCapturedThis: boolean; return transformSourceFile; @@ -1715,28 +1699,18 @@ namespace ts { * * @param node The node to be printed. */ - function onBeforeEmitNode(node: Node) { - previousOnBeforeEmitNode(node); + 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. - pushContainingFunction(node.kind === SyntaxKind.ArrowFunction); + useCapturedThis = node.kind === SyntaxKind.ArrowFunction; } - } - /** - * Called by the printer just after a node is printed. - * - * @param node The node that was printed. - */ - function onAfterEmitNode(node: Node) { - previousOnAfterEmitNode(node); + previousOnEmitNode(node, emit); - if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && isFunctionLike(node)) { - // If we are tracking a captured `this`, pop the last containing function bit. - popContainingFunction(); - } + useCapturedThis = savedUseCapturedThis; } /** @@ -1757,7 +1731,6 @@ namespace ts { function enableSubstitutionsForCapturedThis() { if ((enabledSubstitutions & ES6SubstitutionFlags.CapturedThis) === 0) { enabledSubstitutions |= ES6SubstitutionFlags.CapturedThis; - containingFunctionDepth = 0; context.enableExpressionSubstitution(SyntaxKind.ThisKeyword); context.enableEmitNotification(SyntaxKind.Constructor); context.enableEmitNotification(SyntaxKind.MethodDeclaration); @@ -1850,68 +1823,13 @@ namespace ts { * @param node The ThisKeyword node. */ function substituteThisKeyword(node: PrimaryExpression): PrimaryExpression { - if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && isContainedInArrowFunction()) { + if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && useCapturedThis) { return createIdentifier("_this", /*location*/ node); } return node; } - /** - * Pushes a value onto a stack that indicates whether we are currently printing a node - * within an arrow function. This is used to determine whether we need to capture `this`. - * - * @param isArrowFunction A value indicating whether the current function container is - * an arrow function. - */ - function pushContainingFunction(isArrowFunction: boolean) { - // Encode whether the containing function is an arrow function in the first 31 bits of - // an integer. If the stack grows beyond a depth of 31 functions, use an array. - if (containingFunctionDepth > 0 && containingFunctionDepth % 31 === 0) { - if (!containingFunctionStack) { - containingFunctionStack = [containingFunctionState]; - } - else { - containingFunctionStack.push(containingFunctionState); - } - - containingFunctionState = 0; - } - - if (isArrowFunction) { - containingFunctionState |= 1 << (containingFunctionDepth % 31); - } - - containingFunctionDepth++; - } - - /** - * Pops a value off of the containing function stack. - */ - function popContainingFunction() { - if (containingFunctionDepth > 0) { - containingFunctionDepth--; - if (containingFunctionDepth === 0) { - containingFunctionState = 0; - } - else if (containingFunctionDepth % 31 === 0) { - containingFunctionState = containingFunctionStack.pop(); - } - else { - containingFunctionState &= ~(1 << containingFunctionDepth % 31); - } - } - } - - /** - * Gets a value indicating whether we are currently printing a node inside of an arrow - * function. - */ - function isContainedInArrowFunction() { - return containingFunctionDepth > 0 - && containingFunctionState & (1 << (containingFunctionDepth - 1) % 31); - } - function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) { return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node); } From 72eebdb4d433cb355763294a86c530dada37740d Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 2 Mar 2016 15:50:18 -0800 Subject: [PATCH 33/43] Switched to onEmitNode --- src/compiler/transformers/module/system.ts | 34 ++++++++++------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 4adc37dbc4112..e5d3dad0a7ddc 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -35,7 +35,6 @@ namespace ts { let contextObjectForFile: Identifier; let exportedLocalNames: Identifier[]; let exportedFunctionDeclarations: ExpressionStatement[]; - const noSubstitution: Map = {}; return transformSourceFile; @@ -921,24 +920,23 @@ namespace ts { } function substituteAssignmentExpression(node: BinaryExpression): Expression { - if (!noSubstitution[getNodeId(node)]) { - noSubstitution[getNodeId(node)] = true; - const left = node.left; - switch (left.kind) { - case SyntaxKind.Identifier: - const exportDeclaration = resolver.getReferencedExportContainer(left); - if (exportDeclaration) { - return createExportExpression(left, node); - } - break; + setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution); - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.ArrayLiteralExpression: - if (hasExportedReferenceInDestructuringPattern(left)) { - return substituteDestructuring(node); - } - break; - } + 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; From 1cf183b09aefe2f1e74d6da8b9dd4864f1ed0f5b Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 3 Mar 2016 17:35:27 -0800 Subject: [PATCH 34/43] Fixed invalid assertion in ts transformer --- src/compiler/transformers/ts.ts | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index c69021ce3f0e1..07a572ac41582 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2105,21 +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) - ), - /*location*/ node - ); } function transformInitializedVariable(node: VariableDeclaration): Expression { From d89e21aecd929c5ef9cad8f1cfde148514c3ca1e Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 7 Mar 2016 16:48:40 -0800 Subject: [PATCH 35/43] General fixes and cleanup --- src/compiler/factory.ts | 60 ++++++++-------------- src/compiler/printer.ts | 6 ++- src/compiler/transformers/destructuring.ts | 6 +-- src/compiler/transformers/es6.ts | 14 +++-- src/compiler/transformers/module/module.ts | 2 +- src/compiler/transformers/module/system.ts | 2 +- src/compiler/transformers/ts.ts | 18 +++---- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 7 +-- src/compiler/visitor.ts | 12 ++--- 10 files changed, 60 insertions(+), 68 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 61077ec8f6e9b..88fda6a508009 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -110,21 +110,15 @@ namespace ts { } /** - * 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,18 +128,6 @@ 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; } @@ -153,21 +135,21 @@ namespace ts { * Creates a shallow, memberwise clone of a node for mutation. */ export function getMutableClone(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); + 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); + const clone = getSynthesizedClone(node); + clone.pos = location.pos; + clone.end = location.end; + return clone; } export function createNodeArrayNode(elements: T[]): NodeArrayNode { @@ -718,8 +700,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) { @@ -1154,15 +1136,15 @@ 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 @@ -1370,7 +1352,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; } diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index eb1bb2a658188..6f81d0974a439 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -675,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 { @@ -1411,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; diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 442201a1f7d1e..0e1cbfe1b3a0e 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -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 ebc50ad7b1676..f9c457b430a4f 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -16,6 +16,7 @@ namespace ts { startLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, + getNodeEmitFlags, setNodeEmitFlags, } = context; @@ -50,6 +51,7 @@ namespace ts { function transformSourceFile(node: SourceFile) { currentSourceFile = node; + enclosingBlockScopeContainer = node; return visitEachChild(node, visitor, context); } @@ -772,7 +774,9 @@ namespace ts { enableSubstitutionsForCapturedThis(); } - return transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined); + const func = transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined); + setNodeEmitFlags(func, NodeEmitFlags.CapturesThis); + return func; } /** @@ -1514,7 +1518,7 @@ namespace ts { * @param node A template literal. */ function visitTemplateLiteral(node: LiteralExpression): LeftHandSideExpression { - return createLiteral(node.text); + return createLiteral(node.text, /*location*/ node); } /** @@ -1579,7 +1583,7 @@ namespace ts { // and LineTerminatorSequences are normalized to for both TV and TRV. text = text.replace(/\r\n?/g, "\n"); text = escapeString(text); - return createLiteral(text); + return createLiteral(text, /*location*/ node); } /** @@ -1689,7 +1693,7 @@ namespace ts { addCaptureThisForNodeIfNeeded(statements, node); addRange(statements, visitNodes(createNodeArray(remaining), visitor, isStatement)); addRange(statements, endLexicalEnvironment()); - const clone = cloneNode(node, node, node.flags, /*parent*/ undefined, node); + const clone = getMutableClone(node); clone.statements = createNodeArray(statements, /*location*/ node.statements); return clone; } @@ -1705,7 +1709,7 @@ namespace ts { 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 = node.kind === SyntaxKind.ArrowFunction; + useCapturedThis = (getNodeEmitFlags(node) & NodeEmitFlags.CapturesThis) !== 0; } previousOnEmitNode(node, emit); diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index a1c737fecfdbe..dd2934599ea76 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -750,7 +750,7 @@ namespace ts { } function updateSourceFile(node: SourceFile, statements: Statement[]) { - const updated = cloneNode(node, node, node.flags, /*parent*/ undefined, node); + const updated = getMutableClone(node); updated.statements = createNodeArray(statements, node.statements); return updated; } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index e5d3dad0a7ddc..70e5f2edb9c06 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1271,7 +1271,7 @@ namespace ts { } function updateSourceFile(node: SourceFile, statements: Statement[]) { - const updated = cloneNode(node, node, node.flags, /*parent*/ undefined, node); + const updated = getMutableClone(node); updated.statements = createNodeArray(statements, node.statements); return updated; } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 07a572ac41582..ea9b517391ba0 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -198,8 +198,11 @@ namespace ts { // Fallback to the default visit behavior. return visitorWorker(node); + case SyntaxKind.SemicolonClassElement: + return node; + default: - Debug.fail("Unexpected node."); + Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); break; } } @@ -867,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( @@ -1730,10 +1733,7 @@ namespace ts { ); } else { - return setOriginalNode( - cloneNode(name), - name - ); + return getSynthesizedClone(name); } } @@ -2347,7 +2347,7 @@ namespace ts { function trackChildOfNotEmittedNode(parent: Node, child: T, original: T) { if (!child.parent && !child.original) { - child = cloneNode(child, child, child.flags, child.parent, original); + child = getMutableClone(child); } setNodeEmitFlags(parent, NodeEmitFlags.IsNotEmittedNode); @@ -2403,7 +2403,7 @@ namespace ts { ); if (isNamespaceExport(node)) { - moduleParam = createAssignment(cloneNode(node.name), moduleParam); + moduleParam = createAssignment(getSynthesizedClone(node.name), moduleParam); } currentNamespaceLocalName = getGeneratedNameForNode(node); @@ -2710,7 +2710,7 @@ namespace ts { if (declaration) { const classAlias = currentDecoratedClassAliases[getNodeId(declaration)]; if (classAlias) { - return cloneNode(classAlias); + return getRelocatedClone(classAlias, /*location*/ node); } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f5c4468ef6793..e470bc6df0b03 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2810,6 +2810,7 @@ namespace ts { 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 8ee83810949c0..2ec96b16bc2cd 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1666,11 +1666,11 @@ 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); if (isQualifiedName(clone)) { const { left, right } = clone; clone.left = cloneEntityName(left, clone); - clone.right = cloneNode(right, right, right.flags, parent); + clone.right = getMutableClone(right); } return clone; @@ -3066,7 +3066,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 { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 18b22b5e83f5f..d25d3ded807d5 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -516,7 +516,7 @@ namespace ts { return undefined; } - Debug.assert(test === undefined || test(visited), "Wrong node type after visit."); + Debug.assert(test === undefined || test(visited), "Wrong node type after visit.", () => `Node ${formatSyntaxKind(visited.kind)} did not pass test ${(test).name}.`); aggregateTransformFlags(visited); return visited; } @@ -717,7 +717,7 @@ namespace ts { from = parenthesize(from, parentNode); } - Debug.assert(test === undefined || test(from), "Wrong node type after visit."); + Debug.assert(test === undefined || test(from), "Wrong node type after visit.", () => `Node ${formatSyntaxKind(from.kind)} did not pass test ${(test).name}.`); if (startOnNewLine) { from.startsOnNewLine = true; @@ -774,7 +774,7 @@ namespace ts { */ export function mergeSourceFileLexicalEnvironment(node: SourceFile, declarations: Statement[]) { 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; } @@ -791,7 +791,7 @@ namespace ts { export function mergeModuleDeclarationLexicalEnvironment(node: ModuleDeclaration, declarations: Statement[]) { 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; } @@ -808,7 +808,7 @@ namespace ts { function mergeFunctionLikeLexicalEnvironment(node: FunctionLikeDeclaration, declarations: Statement[]) { 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; } @@ -859,7 +859,7 @@ namespace ts { * @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); + const mutableNode = getMutableClone(node); mutableNode.statements = mergeStatements(node.statements, declarations); return mutableNode; } From 5b8cf9602842de5825ed4a88e41d405b7f9a0c14 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 10 Mar 2016 11:12:24 -0800 Subject: [PATCH 36/43] Moved modifier related flags to separate enum --- src/compiler/binder.ts | 33 ++- src/compiler/checker.ts | 243 +++++++++++---------- src/compiler/declarationEmitter.ts | 57 ++--- src/compiler/emitter.ts | 78 +++---- src/compiler/factory.ts | 13 +- src/compiler/parser.ts | 26 +-- src/compiler/printer.ts | 56 ++--- src/compiler/program.ts | 2 +- src/compiler/transformers/es6.ts | 4 +- src/compiler/transformers/module/module.ts | 16 +- src/compiler/transformers/module/system.ts | 12 +- src/compiler/transformers/ts.ts | 18 +- src/compiler/types.ts | 37 ++-- src/compiler/utilities.ts | 81 +++++-- src/compiler/visitor.ts | 13 +- src/services/breakpoints.ts | 6 +- src/services/navigationBar.ts | 18 +- src/services/services.ts | 42 ++-- src/services/utilities.ts | 18 +- 19 files changed, 400 insertions(+), 373 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f1b6e0053bfaa..b5aec17fe669e 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) && !(getModifierFlags(node) & ModifierFlags.Export)) { return ModuleInstanceState.NonInstantiated; } // 4. other uninstantiated module declarations. @@ -256,7 +256,7 @@ namespace ts { case SyntaxKind.FunctionDeclaration: case SyntaxKind.ClassDeclaration: - return node.flags & NodeFlags.Default ? "default" : undefined; + return getModifierFlags(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 = getModifierFlags(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 (getModifierFlags(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 getModifierFlags(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 (getModifierFlags(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); @@ -1800,7 +1800,7 @@ namespace ts { */ 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) { + if (getModifierFlags(node) & ModifierFlags.Ambient) { return (node.transformFlags = TransformFlags.AssertTypeScript) & ~(node.excludeTransformFlags = TransformFlags.NodeExcludes); } @@ -2008,7 +2008,7 @@ namespace ts { } // If a parameter has an accessibility modifier, then it is TypeScript syntax. - if ((node).flags & NodeFlags.AccessibilityModifier) { + if (getModifierFlags(node) & ModifierFlags.AccessibilityModifier) { transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsParameterPropertyAssignments; } @@ -2033,7 +2033,7 @@ namespace ts { } // An async arrow function is TypeScript syntax. - if (node.flags & NodeFlags.Async) { + if (getModifierFlags(node) & ModifierFlags.Async) { transformFlags |= TransformFlags.AssertTypeScript; } @@ -2052,7 +2052,7 @@ namespace ts { } // An async function expression is TypeScript syntax. - if (node.flags & NodeFlags.Async) { + if (getModifierFlags(node) & ModifierFlags.Async) { transformFlags |= TransformFlags.AssertTypeScript; } @@ -2072,14 +2072,14 @@ namespace ts { // 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 + || getModifierFlags(node) & ModifierFlags.Export || subtreeFlags & TransformFlags.ContainsCapturedLexicalThis || subtreeFlags & TransformFlags.ContainsDefaultValueAssignments) { transformFlags |= TransformFlags.AssertES6; } // An async function declaration is TypeScript syntax. - if (node.flags & NodeFlags.Async) { + if (getModifierFlags(node) & ModifierFlags.Async) { transformFlags |= TransformFlags.AssertTypeScript; } @@ -2103,7 +2103,7 @@ namespace ts { case SyntaxKind.VariableStatement: // If a VariableStatement is exported, then it is either ES6 or TypeScript syntax. - if (node.flags & NodeFlags.Export) { + if (getModifierFlags(node) & ModifierFlags.Export) { transformFlags |= TransformFlags.AssertES6 | TransformFlags.AssertTypeScript; } @@ -2204,8 +2204,7 @@ namespace ts { // 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 + || getModifierFlags(node) & (ModifierFlags.Async | ModifierFlags.Abstract) || (subtreeFlags & TransformFlags.ContainsDecorators && subtreeFlags & TransformFlags.ContainsComputedPropertyName)) { transformFlags |= TransformFlags.AssertTypeScript; @@ -2220,7 +2219,7 @@ 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 || + if (getModifierFlags(node) & ModifierFlags.Abstract || subtreeFlags & TransformFlags.ContainsDecorators && subtreeFlags & TransformFlags.ContainsComputedPropertyName) { transformFlags |= TransformFlags.AssertTypeScript; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cf90007062e14..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,7 +7494,7 @@ 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 (container.kind === SyntaxKind.MethodDeclaration && getModifierFlags(container) & ModifierFlags.Async) { if (isSuperProperty(node.parent) && isAssignmentTarget(node.parent)) { getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding; } @@ -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) { @@ -16832,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) { @@ -17138,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/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 d279c419b530a..ad942cdca5c10 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -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 88fda6a508009..2de1f3dbdade0 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -51,20 +51,13 @@ namespace ts { } 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; @@ -78,7 +71,6 @@ namespace ts { } array.arrayKind = ArrayKind.ModifiersArray; - array.flags = flags; return array; } @@ -86,7 +78,6 @@ namespace ts { if (modifiers) { const array = createModifiersArray(modifiers); node.modifiers = array; - node.flags |= array.flags; } else { node.modifiers = undefined; @@ -1079,9 +1070,9 @@ namespace ts { thisArg = createThis(/*location*/ callee.expression); target = callee; } - else if (isSuperCall(callee)) { + else if (callee.kind === SyntaxKind.SuperKeyword) { thisArg = createThis(/*location*/ callee); - target = languageVersion < ScriptTarget.ES6 ? createIdentifier("_super", /*location*/ callee) : callee; + target = languageVersion < ScriptTarget.ES6 ? createIdentifier("_super", /*location*/ callee) : callee; } else { switch (callee.kind) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c4813ac993217..24d9b32fde37b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1009,7 +1009,6 @@ namespace ts { array.pos = pos; array.end = pos; array.arrayKind = ArrayKind.ModifiersArray; - array.flags = 0; return array; } @@ -2021,7 +2020,6 @@ namespace ts { function setModifiers(node: Node, modifiers: ModifiersArray) { if (modifiers) { - node.flags |= modifiers.flags; node.modifiers = modifiers; } } @@ -2826,7 +2824,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. @@ -2972,7 +2970,7 @@ namespace ts { function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction { const node = createNode(SyntaxKind.ArrowFunction); setModifiers(node, parseModifiersForArrowFunction()); - const isAsync = !!(node.flags & NodeFlags.Async); + const isAsync = !!(getModifierFlags(node) & ModifierFlags.Async); // Arrow functions are never generators. // @@ -4032,7 +4030,7 @@ namespace ts { 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) : @@ -4765,9 +4763,9 @@ namespace ts { setModifiers(node, 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)); @@ -4791,7 +4789,7 @@ namespace ts { 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)); @@ -4814,7 +4812,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); @@ -4964,7 +4962,6 @@ namespace ts { * In such situations, 'permitInvalidConstAsModifier' should be set to true. */ function parseModifiers(permitInvalidConstAsModifier?: boolean): ModifiersArray { - let flags = 0; let modifiers: ModifiersArray; while (true) { const modifierStart = scanner.getStartPos(); @@ -4983,7 +4980,6 @@ namespace ts { } } - flags |= modifierToFlag(modifierKind); const modifier = finishNode(createNode(modifierKind, modifierStart)); if (!modifiers) { modifiers = createModifiersArray([modifier], modifierStart); @@ -4993,7 +4989,6 @@ namespace ts { } } if (modifiers) { - modifiers.flags = flags; modifiers.end = scanner.getStartPos(); } return modifiers; @@ -5007,7 +5002,6 @@ namespace ts { nextToken(); const modifier = finishNode(createNode(modifierKind, modifierStart)); modifiers = createModifiersArray([modifier], modifierStart); - modifiers.flags = modifierToFlag(modifierKind); modifiers.end = scanner.getStartPos(); } @@ -5223,7 +5217,7 @@ namespace ts { 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); } @@ -5245,7 +5239,7 @@ namespace ts { } function parseModuleDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): ModuleDeclaration { - let flags = modifiers ? modifiers.flags : 0; + let flags: NodeFlags = 0; if (token === SyntaxKind.GlobalKeyword) { // global augmentation return parseAmbientExternalModuleDeclaration(fullStart, decorators, modifiers); @@ -5541,7 +5535,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 6f81d0974a439..9ef096b4d2e36 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) { @@ -2520,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..f3a7682ea04e2 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -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/transformers/es6.ts b/src/compiler/transformers/es6.ts index f9c457b430a4f..2568becac654b 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1680,7 +1680,7 @@ namespace ts { function visitSuperKeyword(node: PrimaryExpression): LeftHandSideExpression { return containingNonArrowFunction && isClassElement(containingNonArrowFunction) - && (containingNonArrowFunction.flags & NodeFlags.Static) === 0 + && !hasModifier(containingNonArrowFunction, ModifierFlags.Static) ? createPropertyAccess(createIdentifier("_super"), "prototype") : createIdentifier("_super"); } @@ -1840,7 +1840,7 @@ namespace ts { function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) { const expression = getDeclarationName(node); - return member.flags & NodeFlags.Static ? expression : createPropertyAccess(expression, "prototype"); + return hasModifier(member, ModifierFlags.Static) ? expression : createPropertyAccess(expression, "prototype"); } function hasSynthesizedDefaultSuperCall(constructor: ConstructorDeclaration, hasExtendsClause: boolean) { diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index dd2934599ea76..f910e543bb1dc 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -291,7 +291,7 @@ namespace ts { if (contains(externalImports, node)) { const statements: Statement[] = []; if (moduleKind !== ModuleKind.AMD) { - if (node.flags & NodeFlags.Export) { + if (hasModifier(node, ModifierFlags.Export)) { addNode(statements, createStatement( createExportAssignment( @@ -318,7 +318,7 @@ namespace ts { } } else { - if (node.flags & NodeFlags.Export) { + if (hasModifier(node, ModifierFlags.Export)) { addNode(statements, createStatement( createExportAssignment(node.name, node.name), @@ -505,7 +505,7 @@ namespace ts { function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMany { const statements: Statement[] = []; if (node.name) { - if (node.flags & NodeFlags.Export) { + if (hasModifier(node, ModifierFlags.Export)) { addNode(statements, createFunctionDeclaration( /*modifiers*/ undefined, @@ -517,7 +517,7 @@ namespace ts { ) ); - if (node.flags & NodeFlags.Default) { + if (hasModifier(node, ModifierFlags.Default)) { addExportDefault(statements, getSynthesizedClone(node.name), /*location*/ node); } } @@ -528,7 +528,7 @@ namespace ts { addExportMemberAssignments(statements, node.name); } else { - Debug.assert((node.flags & NodeFlags.Default) !== 0); + Debug.assert(hasModifier(node, ModifierFlags.Default)); addExportDefault(statements, createFunctionExpression( /*asteriskToken*/ undefined, @@ -546,7 +546,7 @@ namespace ts { function visitClassDeclaration(node: ClassDeclaration): OneOrMany { const statements: Statement[] = []; if (node.name) { - if (node.flags & NodeFlags.Export) { + if (hasModifier(node, ModifierFlags.Export)) { addNode(statements, createClassDeclaration( /*modifiers*/ undefined, @@ -557,7 +557,7 @@ namespace ts { ) ); - if (node.flags & NodeFlags.Default) { + if (hasModifier(node, ModifierFlags.Default)) { addExportDefault(statements, getSynthesizedClone(node.name), /*location*/ node); } } @@ -568,7 +568,7 @@ namespace ts { addExportMemberAssignments(statements, node.name); } else { - Debug.assert((node.flags & NodeFlags.Default) !== 0); + Debug.assert(hasModifier(node, ModifierFlags.Default)); addExportDefault(statements, createClassExpression( /*name*/ undefined, diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 70e5f2edb9c06..2b51fb1119d7b 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -542,7 +542,7 @@ namespace ts { * @param node The variable statement to visit. */ function visitVariableStatement(node: VariableStatement): OneOrMany { - const isExported = (node.flags & NodeFlags.Export) !== 0; + const isExported = hasModifier(node, ModifierFlags.Export); const expressions: Expression[] = []; for (const variable of node.declarationList.declarations) { addNode(expressions, transformVariable(variable, isExported)); @@ -587,7 +587,7 @@ namespace ts { * @param node The function declaration to visit. */ function visitFunctionDeclaration(node: FunctionDeclaration): Node { - if (node.flags & NodeFlags.Export) { + 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( @@ -601,7 +601,7 @@ namespace ts { // Record a declaration export in the outer module body function. recordExportedFunctionDeclaration(node); - if ((node.flags & NodeFlags.Default) === 0) { + if (!hasModifier(node, ModifierFlags.Default)) { recordExportName(name); } } @@ -640,8 +640,8 @@ namespace ts { ); // If the class was exported, write a declaration export to the inner module body function. - if (node.flags & NodeFlags.Export) { - if ((node.flags & NodeFlags.Default) === 0) { + if (hasModifier(node, ModifierFlags.Export)) { + if (!hasModifier(node, ModifierFlags.Default)) { recordExportName(name); } @@ -1179,7 +1179,7 @@ namespace ts { */ function createDeclarationExport(node: DeclarationStatement) { const declarationName = getDeclarationName(node); - const exportName = node.flags & NodeFlags.Default ? createLiteral("default") : declarationName; + const exportName = hasModifier(node, ModifierFlags.Default) ? createLiteral("default") : declarationName; return createExportStatement(exportName, declarationName); } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index ea9b517391ba0..951e800f90de3 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -213,7 +213,7 @@ namespace ts { * @param node The node to visit. */ function visitTypeScript(node: Node): Node { - if (node.flags & NodeFlags.Ambient) { + if (hasModifier(node, ModifierFlags.Ambient)) { // TypeScript ambient declarations are elided. return undefined; } @@ -858,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); } @@ -917,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; } @@ -1015,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); } /** @@ -1905,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); } /** @@ -2534,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); } /** @@ -2562,7 +2562,7 @@ namespace ts { */ function isNamedExternalModuleExport(node: Node) { return isExternalModuleExport(node) - && (node.flags & NodeFlags.Default) === 0; + && hasModifier(node, ModifierFlags.Default); } /** @@ -2572,7 +2572,7 @@ namespace ts { */ function isDefaultExternalModuleExport(node: Node) { return isExternalModuleExport(node) - && (node.flags & NodeFlags.Default) !== 0; + && hasModifier(node, ModifierFlags.Default); } /** @@ -2624,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); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e470bc6df0b03..230e870a28a6f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -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,24 @@ 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 + + AccessibilityModifier = Public | Private | Protected, + NonPublicAccessibilityModifier = Private | Protected, + } + export const enum JsxFlags { None = 0, /** An element from a named property of the JSX.IntrinsicElements interface */ @@ -482,7 +489,7 @@ namespace ts { } export interface ModifiersArray extends NodeArray { - flags: number; + // flags: number; } // @kind(SyntaxKind.AbstractKeyword) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2ec96b16bc2cd..b38d5d5f43175 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -491,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 @@ -519,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 { @@ -1390,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; @@ -1558,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 { @@ -1667,10 +1687,12 @@ namespace ts { */ export function cloneEntityName(node: EntityName, parent?: Node): EntityName { const clone = getMutableClone(node); + clone.parent = parent; if (isQualifiedName(clone)) { const { left, right } = clone; clone.left = cloneEntityName(left, clone); clone.right = getMutableClone(right); + clone.right.parent = clone; } return clone; @@ -2301,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) { @@ -2534,21 +2556,42 @@ 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 { + 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; + } + 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 { @@ -2608,7 +2651,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) { @@ -3673,6 +3716,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 d25d3ded807d5..f2e97b7365e1f 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -611,17 +611,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 { @@ -631,12 +626,6 @@ namespace ts { if (updated !== undefined || visited !== value) { if (updated === undefined) { updated = getMutableClone(node); - updated.flags &= ~NodeFlags.Modifier; - } - - if (modifiers) { - updated.flags |= modifiers; - modifiers = undefined; } if (visited !== value) { @@ -927,7 +916,7 @@ 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)) { + if (hasModifier(node, ModifierFlags.Ambient) || isTypeNode(node)) { return 0; } 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; From 99e6ad8b63aade7909898ec5719bc05fdcfae9bf Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 10 Mar 2016 11:23:17 -0800 Subject: [PATCH 37/43] Removed ModifiersArray --- src/compiler/factory.ts | 53 ++--------------- src/compiler/parser.ts | 118 ++++++++++++++++---------------------- src/compiler/printer.ts | 2 +- src/compiler/program.ts | 2 +- src/compiler/types.ts | 12 +--- src/compiler/utilities.ts | 7 +-- src/compiler/visitor.ts | 4 +- 7 files changed, 62 insertions(+), 136 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 2de1f3dbdade0..cf333af3f0031 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -46,46 +46,9 @@ namespace ts { array.hasTrailingComma = true; } - array.arrayKind = ArrayKind.NodeArray; return array; } - export function createModifiersArray(elements?: Modifier[], location?: TextRange): ModifiersArray { - if (elements) { - if (isModifiersArray(elements)) { - return elements; - } - } - else { - elements = []; - } - - const array = elements; - if (location) { - array.pos = location.pos; - array.end = location.end; - } - else { - array.pos = -1; - array.end = -1; - } - - array.arrayKind = ArrayKind.ModifiersArray; - return array; - } - - export function setModifiers(node: T, modifiers: Modifier[]) { - if (modifiers) { - const array = createModifiersArray(modifiers); - node.modifiers = array; - } - else { - node.modifiers = undefined; - } - - return node; - } - export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node { const node = createNode(kind, /*location*/ undefined); node.startsOnNewLine = startsOnNewLine; @@ -96,10 +59,6 @@ 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 with no source map location. */ @@ -238,7 +197,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); @@ -260,7 +219,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(); @@ -271,7 +230,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]); @@ -463,7 +422,7 @@ namespace ts { 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; } @@ -562,7 +521,7 @@ namespace ts { 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; @@ -578,7 +537,7 @@ namespace ts { 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); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 24d9b32fde37b..14c701fb09e64 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -997,18 +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; return array; } @@ -2018,16 +2006,10 @@ namespace ts { return token === SyntaxKind.DotDotDotToken || isIdentifierOrPattern() || isModifierKind(token) || token === SyntaxKind.AtToken; } - function setModifiers(node: Node, modifiers: ModifiersArray) { - if (modifiers) { - 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]: @@ -2216,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; @@ -2244,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(); @@ -2969,7 +2951,7 @@ namespace ts { function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction { const node = createNode(SyntaxKind.ArrowFunction); - setModifiers(node, parseModifiersForArrowFunction()); + node.modifiers = parseModifiersForArrowFunction(); const isAsync = !!(getModifierFlags(node) & ModifierFlags.Async); // Arrow functions are never generators. @@ -3940,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); } @@ -4025,7 +4007,7 @@ namespace ts { } const node = createNode(SyntaxKind.FunctionExpression); - setModifiers(node, parseModifiers()); + node.modifiers = parseModifiers(); parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); @@ -4613,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); } } @@ -4748,19 +4730,19 @@ 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 = hasModifier(node, ModifierFlags.Default) ? parseOptionalIdentifier() : parseIdentifier(); @@ -4771,20 +4753,20 @@ namespace ts { 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; @@ -4795,10 +4777,10 @@ namespace ts { 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(); @@ -4820,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(); @@ -4839,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); @@ -4961,8 +4943,8 @@ namespace ts { * * In such situations, 'permitInvalidConstAsModifier' should be set to true. */ - function parseModifiers(permitInvalidConstAsModifier?: boolean): ModifiersArray { - let modifiers: ModifiersArray; + function parseModifiers(permitInvalidConstAsModifier?: boolean): NodeArray { + let modifiers: NodeArray; while (true) { const modifierStart = scanner.getStartPos(); const modifierKind = token; @@ -4982,7 +4964,7 @@ namespace ts { const modifier = finishNode(createNode(modifierKind, modifierStart)); if (!modifiers) { - modifiers = createModifiersArray([modifier], modifierStart); + modifiers = createNodeArray([modifier], modifierStart); } else { modifiers.push(modifier); @@ -4994,14 +4976,14 @@ namespace ts { 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 = createNodeArray([modifier], modifierStart); modifiers.end = scanner.getStartPos(); } @@ -5061,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(); @@ -5143,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(); @@ -5155,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(); @@ -5179,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)) { @@ -5207,13 +5189,13 @@ 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) @@ -5222,10 +5204,10 @@ namespace ts { 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(); @@ -5238,7 +5220,7 @@ namespace ts { return finishNode(node); } - function parseModuleDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): ModuleDeclaration { + function parseModuleDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ModuleDeclaration { let flags: NodeFlags = 0; if (token === SyntaxKind.GlobalKeyword) { // global augmentation @@ -5269,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(); @@ -5282,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(); @@ -5294,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 ; @@ -5430,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(); @@ -5453,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; } diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 9ef096b4d2e36..c97f35599161e 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -1958,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(" "); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index f3a7682ea04e2..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) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 230e870a28a6f..c03fd2744f5e8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -453,7 +453,7 @@ namespace ts { /* @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. @@ -465,13 +465,7 @@ 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; } @@ -488,10 +482,6 @@ namespace ts { nodes: NodeArray; } - export interface ModifiersArray extends NodeArray { - // flags: number; - } - // @kind(SyntaxKind.AbstractKeyword) // @kind(SyntaxKind.AsyncKeyword) // @kind(SyntaxKind.ConstKeyword) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b38d5d5f43175..7d37d2bf96b6d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2987,11 +2987,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 diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index f2e97b7365e1f..75377fc38cd41 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -580,9 +580,7 @@ namespace ts { } if (updated !== undefined) { - return isModifiersArray(nodes) - ? createModifiersArray(updated, nodes) - : createNodeArray(updated, nodes, nodes.hasTrailingComma); + return createNodeArray(updated, nodes, nodes.hasTrailingComma); } return nodes; From 47cdfbe55c593193852b20b3d714ad08fedb41cc Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 10 Mar 2016 11:40:57 -0800 Subject: [PATCH 38/43] Add support for array return values from visitors --- src/compiler/transformers/destructuring.ts | 10 +-- src/compiler/transformers/es6.ts | 11 ++-- src/compiler/transformers/es7.ts | 4 +- src/compiler/transformers/jsx.ts | 4 +- src/compiler/transformers/module/module.ts | 2 +- src/compiler/transformers/module/system.ts | 4 +- src/compiler/transformers/ts.ts | 28 ++++----- src/compiler/visitor.ts | 71 ++++++++++++---------- 8 files changed, 71 insertions(+), 63 deletions(-) diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 0e1cbfe1b3a0e..aae8fbd8ce075 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) => OneOrMany) { 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) => OneOrMany) { 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) => OneOrMany) { 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) => OneOrMany) { 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) => OneOrMany) { if (value && visitor) { value = visitNode(value, visitor, isExpression); } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 2568becac654b..8fe144a6d6247 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -55,7 +55,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitor(node: Node): Node { + function visitor(node: Node): OneOrMany { const savedContainingNonArrowFunction = containingNonArrowFunction; const savedCurrentParent = currentParent; const savedCurrentNode = currentNode; @@ -63,17 +63,18 @@ namespace ts { const savedEnclosingBlockScopeContainerParent = enclosingBlockScopeContainerParent; onBeforeVisitNode(node); - node = visitorWorker(node); + + const visited = visitorWorker(node); containingNonArrowFunction = savedContainingNonArrowFunction; currentParent = savedCurrentParent; currentNode = savedCurrentNode; enclosingBlockScopeContainer = savedEnclosingBlockScopeContainer; enclosingBlockScopeContainerParent = savedEnclosingBlockScopeContainerParent; - return node; + return visited; } - function visitorWorker(node: Node): Node { + function visitorWorker(node: Node): OneOrMany { if (node.transformFlags & TransformFlags.ES6) { return visitJavaScript(node); } @@ -85,7 +86,7 @@ namespace ts { } } - function visitJavaScript(node: Node): Node { + function visitJavaScript(node: Node): OneOrMany { switch (node.kind) { case SyntaxKind.ClassDeclaration: return visitClassDeclaration(node); diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts index 7be97566c51e8..fc6e878799f35 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -12,7 +12,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitor(node: Node): Node { + function visitor(node: Node): OneOrMany { if (node.transformFlags & TransformFlags.ES7) { return visitorWorker(node); } @@ -24,7 +24,7 @@ namespace ts { } } - function visitorWorker(node: Node) { + function visitorWorker(node: Node): OneOrMany { switch (node.kind) { case SyntaxKind.BinaryExpression: return visitBinaryExpression(node); diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 4dbfabb2f7206..3a68f1f2e28d4 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -18,7 +18,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitor(node: Node): Node { + function visitor(node: Node): OneOrMany { if (node.transformFlags & TransformFlags.Jsx) { return visitorWorker(node); } @@ -30,7 +30,7 @@ namespace ts { } } - function visitorWorker(node: Node): Node { + function visitorWorker(node: Node): OneOrMany { switch (node.kind) { case SyntaxKind.JsxElement: return visitJsxElement(node); diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index f910e543bb1dc..632514f2a2729 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -173,7 +173,7 @@ namespace ts { * * @param node The node. */ - function visitor(node: Node) { + function visitor(node: Node): OneOrMany { switch (node.kind) { case SyntaxKind.ImportDeclaration: return visitImportDeclaration(node); diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 2b51fb1119d7b..9920878c15956 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -408,7 +408,7 @@ namespace ts { return createArrayLiteral(setters); } - function visitSourceElement(node: Node): Node { + function visitSourceElement(node: Node): OneOrMany { switch (node.kind) { case SyntaxKind.ImportDeclaration: return visitImportDeclaration(node); @@ -427,7 +427,7 @@ namespace ts { } } - function visitNestedNode(node: Node): Node { + function visitNestedNode(node: Node): OneOrMany { switch (node.kind) { case SyntaxKind.VariableStatement: return visitVariableStatement(node); diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 951e800f90de3..401ccb1787320 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) => OneOrMany): OneOrMany { // 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): OneOrMany { 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): OneOrMany { 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): OneOrMany { 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): OneOrMany { 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): OneOrMany { return visitWithStack(node, classElementVisitorWorker); } @@ -182,7 +182,7 @@ namespace ts { * * @param node The node to visit. */ - function classElementVisitorWorker(node: Node) { + function classElementVisitorWorker(node: Node): OneOrMany { switch (node.kind) { case SyntaxKind.Constructor: // TypeScript constructors are transformed in `transformClassDeclaration`. @@ -212,7 +212,7 @@ namespace ts { * * @param node The node to visit. */ - function visitTypeScript(node: Node): Node { + function visitTypeScript(node: Node): OneOrMany { if (hasModifier(node, ModifierFlags.Ambient)) { // TypeScript ambient declarations are elided. return undefined; diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 75377fc38cd41..92307b707f8a7 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 OneOrMany = T | NodeArrayNode | T[]; /** * Describes an edge of a Node, used when traversing a syntax tree. @@ -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) => OneOrMany, test: (node: Node) => boolean, optional?: boolean, lift?: (node: NodeArray) => T): T { return visitNodeWorker(node, visitor, test, optional, lift, /*parenthesize*/ undefined, /*parentNode*/ undefined); } @@ -493,7 +493,7 @@ 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) => OneOrMany, test: (node: Node) => boolean, optional: boolean, lift: (node: NodeArray) => Node, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): Node { if (node === undefined) { return undefined; } @@ -503,22 +503,29 @@ namespace ts { return node; } - if (visited !== undefined && isNodeArrayNode(visited)) { - visited = (lift || extractSingleNode)((>visited).nodes); + if (visited === undefined) { + Debug.assert(optional, "Node not optional."); + return undefined; } - if (parenthesize !== undefined && visited !== undefined) { - visited = parenthesize(visited, parentNode); + let visitedNode: Node; + if (isArray(visited)) { + visitedNode = (lift || extractSingleNode)(>visited); + } + else if (isNodeArrayNode(visited)) { + visitedNode = (lift || extractSingleNode)((>visited).nodes); + } + 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.", () => `Node ${formatSyntaxKind(visited.kind)} did not pass test ${(test).name}.`); - aggregateTransformFlags(visited); - return visited; + Debug.assert(test === undefined || test(visitedNode), "Wrong node type after visit.", () => `Node ${formatSyntaxKind(visitedNode.kind)} did not pass test ${(test).name}.`); + aggregateTransformFlags(visitedNode); + return visitedNode; } /** @@ -530,7 +537,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) => OneOrMany, test: (node: Node) => boolean, start?: number, count?: number): TArray { return visitNodesWorker(nodes, visitor, test, /*parenthesize*/ undefined, /*parentNode*/ undefined, start, count); } @@ -543,7 +550,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. */ - 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) => OneOrMany, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, start: number, count: number): NodeArray { if (nodes === undefined) { return undefined; } @@ -593,8 +600,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) => OneOrMany, context: LexicalEnvironment): T; + export function visitEachChild(node: T & Map, visitor: (node: Node) => OneOrMany, context: LexicalEnvironment): T { if (node === undefined) { return undefined; } @@ -661,7 +668,7 @@ namespace ts { if (nodes) { for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; - if (result || node === undefined || isNodeArrayNode(node)) { + if (result || node === undefined || isArray(node) || isNodeArrayNode(node)) { if (!result) { result = nodes.slice(0, i); } @@ -695,26 +702,26 @@ namespace ts { function addNodeWorker(to: Node[], from: OneOrMany, startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean) { 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); } - - if (parenthesize !== undefined) { - from = parenthesize(from, parentNode); + else if (isNodeArrayNode(from)) { + addNodesWorker(to, from.nodes, startOnNewLine, test, parenthesize, parentNode, isVisiting); } + else { + const node = parenthesize !== undefined ? parenthesize(from, parentNode) : from; + Debug.assert(test === undefined || test(node), "Wrong node type after visit.", () => `Node ${formatSyntaxKind(node.kind)} did not pass test ${(test).name}.`); - Debug.assert(test === undefined || test(from), "Wrong node type after visit.", () => `Node ${formatSyntaxKind(from.kind)} did not pass test ${(test).name}.`); + if (startOnNewLine) { + node.startsOnNewLine = true; + } - if (startOnNewLine) { - from.startsOnNewLine = true; - } + if (isVisiting) { + aggregateTransformFlags(node); + } - if (isVisiting) { - aggregateTransformFlags(from); + to.push(node); } - - to.push(from); } } From 2699bf923b1d7f5e2e465afe082c2329789a19da Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 10 Mar 2016 11:45:20 -0800 Subject: [PATCH 39/43] Removed NodeArrayNode in favor of arrays --- src/compiler/factory.ts | 6 ------ src/compiler/transformers/es6.ts | 4 +--- src/compiler/transformers/module/module.ts | 12 ++++++------ src/compiler/transformers/module/system.ts | 4 ++-- src/compiler/transformers/ts.ts | 16 ++++++++-------- src/compiler/types.ts | 15 +-------------- src/compiler/utilities.ts | 7 ------- src/compiler/visitor.ts | 10 ++-------- 8 files changed, 20 insertions(+), 54 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index cf333af3f0031..84eb1ac62ccd8 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -102,12 +102,6 @@ namespace ts { return clone; } - export function createNodeArrayNode(elements: T[]): NodeArrayNode { - const node = >createSynthesizedNode(SyntaxKind.NodeArrayNode); - node.nodes = createNodeArray(elements); - return node; - } - // Literals export function createLiteral(value: string, location?: TextRange): StringLiteral; diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 8fe144a6d6247..d0fa179372bb7 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1047,9 +1047,7 @@ namespace ts { // If we are here it is because the name contains a binding pattern. Debug.assert(isBindingPattern(node.name)); - return createNodeArrayNode( - flattenVariableDestructuring(node, /*value*/ undefined, visitor) - ); + return flattenVariableDestructuring(node, /*value*/ undefined, visitor); } function visitLabeledStatement(node: LabeledStatement) { diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 632514f2a2729..a258cb914bb39 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -281,7 +281,7 @@ namespace ts { } addExportImportAssignments(statements, node); - return createNodeArrayNode(statements); + return statements; } return undefined; @@ -329,7 +329,7 @@ namespace ts { } addExportImportAssignments(statements, node); - return createNodeArrayNode(statements); + return statements; } return undefined; @@ -370,7 +370,7 @@ namespace ts { } } - return createNodeArrayNode(statements); + return statements; } else { // export * from "mod"; @@ -395,7 +395,7 @@ namespace ts { if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { const statements: Statement[] = []; addExportDefault(statements, node.expression, /*location*/ node); - return createNodeArrayNode(statements); + return statements; } return undefined; @@ -540,7 +540,7 @@ namespace ts { /*location*/ node ); } - return createNodeArrayNode(statements); + return statements; } function visitClassDeclaration(node: ClassDeclaration): OneOrMany { @@ -580,7 +580,7 @@ namespace ts { ); } - return createNodeArrayNode(statements); + return statements; } function substituteExpression(node: Expression) { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 9920878c15956..1ba5d365fcf99 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -506,7 +506,7 @@ namespace ts { if (!node.moduleSpecifier) { const statements: Statement[] = []; addNodes(statements, map(node.exportClause.elements, visitExportSpecifier)); - return createNodeArrayNode(statements); + return statements; } return undefined; @@ -648,7 +648,7 @@ namespace ts { addNode(statements, createDeclarationExport(node)); } - return createNodeArrayNode(statements); + return statements; } /** diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 401ccb1787320..b92e98d7cd499 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -408,7 +408,7 @@ namespace ts { * * @param node The node to transform. */ - function visitClassDeclaration(node: ClassDeclaration): NodeArrayNode { + function visitClassDeclaration(node: ClassDeclaration): OneOrMany { const staticProperties = getInitializedProperties(node, /*isStatic*/ true); const hasExtendsClause = getClassExtendsHeritageClauseElement(node) !== undefined; @@ -471,7 +471,7 @@ namespace ts { } } - return createNodeArrayNode(statements); + return statements; } /** @@ -1870,10 +1870,10 @@ namespace ts { ); if (isNamespaceExport(node)) { - return createNodeArrayNode([ + return [ func, createNamespaceExport(getSynthesizedClone(node.name), getSynthesizedClone(node.name)) - ]); + ]; } return func; @@ -2150,7 +2150,7 @@ namespace ts { * * @param node The enum declaration node. */ - function visitEnumDeclaration(node: EnumDeclaration) { + function visitEnumDeclaration(node: EnumDeclaration): OneOrMany { if (shouldElideEnumDeclaration(node)) { return undefined; } @@ -2215,7 +2215,7 @@ namespace ts { } currentNamespaceLocalName = savedCurrentNamespaceLocalName; - return createNodeArrayNode(statements); + return statements; } /** @@ -2362,7 +2362,7 @@ namespace ts { * * @param node The module declaration node. */ - function visitModuleDeclaration(node: ModuleDeclaration) { + function visitModuleDeclaration(node: ModuleDeclaration): OneOrMany { if (shouldElideModuleDeclaration(node)) { return undefined; } @@ -2435,7 +2435,7 @@ namespace ts { ); currentNamespaceLocalName = savedCurrentNamespaceLocalName; - return createNodeArrayNode(statements); + return statements; } /** diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c03fd2744f5e8..7e9dafabe7af4 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 @@ -469,19 +469,6 @@ namespace ts { 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; - } - // @kind(SyntaxKind.AbstractKeyword) // @kind(SyntaxKind.AsyncKeyword) // @kind(SyntaxKind.ConstKeyword) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7d37d2bf96b6d..ca8e4d5289e8c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3471,13 +3471,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 { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 92307b707f8a7..5eec55005faec 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -3,7 +3,7 @@ /* @internal */ namespace ts { - export type OneOrMany = T | NodeArrayNode | T[]; + export type OneOrMany = T | T[]; /** * Describes an edge of a Node, used when traversing a syntax tree. @@ -512,9 +512,6 @@ namespace ts { if (isArray(visited)) { visitedNode = (lift || extractSingleNode)(>visited); } - else if (isNodeArrayNode(visited)) { - visitedNode = (lift || extractSingleNode)((>visited).nodes); - } else { visitedNode = visited; } @@ -668,7 +665,7 @@ namespace ts { if (nodes) { for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; - if (result || node === undefined || isArray(node) || isNodeArrayNode(node)) { + if (result || node === undefined || isArray(node)) { if (!result) { result = nodes.slice(0, i); } @@ -705,9 +702,6 @@ namespace ts { if (isArray(from)) { addNodesWorker(to, from, startOnNewLine, test, parenthesize, parentNode, isVisiting); } - else if (isNodeArrayNode(from)) { - addNodesWorker(to, from.nodes, startOnNewLine, test, parenthesize, parentNode, isVisiting); - } else { const node = parenthesize !== undefined ? parenthesize(from, parentNode) : from; Debug.assert(test === undefined || test(node), "Wrong node type after visit.", () => `Node ${formatSyntaxKind(node.kind)} did not pass test ${(test).name}.`); From 94018a13e6900981ece892727e35f20a9f19d88d Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 10 Mar 2016 12:47:10 -0800 Subject: [PATCH 40/43] cleaned up assertions and removed flattenNodes. --- src/compiler/core.ts | 18 ++- src/compiler/transformers/destructuring.ts | 10 +- src/compiler/transformers/es6.ts | 19 +-- src/compiler/transformers/es7.ts | 13 +- src/compiler/transformers/jsx.ts | 16 ++- src/compiler/transformers/module/module.ts | 20 +-- src/compiler/transformers/module/system.ts | 10 +- src/compiler/transformers/ts.ts | 36 ++--- src/compiler/types.ts | 2 + src/compiler/visitor.ts | 147 ++++++++++++--------- 10 files changed, 171 insertions(+), 120 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index aeb908c20cc72..c9a0da63cfe73 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1035,7 +1035,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; @@ -1055,6 +1060,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/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index aae8fbd8ce075..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) => OneOrMany) { + 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) => OneOrMany) { + 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) => OneOrMany) { + 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) => OneOrMany) { + 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) => OneOrMany) { + visitor?: (node: Node) => VisitResult) { if (value && visitor) { value = visitNode(value, visitor, isExpression); } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index d0fa179372bb7..41850d0db161c 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -55,7 +55,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitor(node: Node): OneOrMany { + function visitor(node: Node): VisitResult { const savedContainingNonArrowFunction = containingNonArrowFunction; const savedCurrentParent = currentParent; const savedCurrentNode = currentNode; @@ -74,7 +74,7 @@ namespace ts { return visited; } - function visitorWorker(node: Node): OneOrMany { + function visitorWorker(node: Node): VisitResult { if (node.transformFlags & TransformFlags.ES6) { return visitJavaScript(node); } @@ -86,7 +86,7 @@ namespace ts { } } - function visitJavaScript(node: Node): OneOrMany { + function visitJavaScript(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.ClassDeclaration: return visitClassDeclaration(node); @@ -174,9 +174,12 @@ namespace ts { case SyntaxKind.SourceFile: return visitSourceFileNode(node); + + default: + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); } - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); } function onBeforeVisitNode(node: Node) { @@ -695,7 +698,7 @@ namespace ts { break; default: - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + Debug.failBadSyntaxKind(node); break; } } @@ -936,7 +939,7 @@ namespace ts { enableSubstitutionsForBlockScopedBindings(); return setOriginalNode( createVariableDeclarationList( - flattenNodes(map(node.declarations, visitVariableDeclarationInLetDeclarationList)), + flatten(map(node.declarations, visitVariableDeclarationInLetDeclarationList)), /*location*/ node ), node @@ -1043,7 +1046,7 @@ namespace ts { * * @param node A VariableDeclaration node. */ - function visitVariableDeclaration(node: VariableDeclaration): OneOrMany { + function visitVariableDeclaration(node: VariableDeclaration): VisitResult { // If we are here it is because the name contains a binding pattern. Debug.assert(isBindingPattern(node.name)); @@ -1293,7 +1296,7 @@ namespace ts { break; default: - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + Debug.failBadSyntaxKind(node); break; } } diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts index fc6e878799f35..d04ca55db4b31 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -12,7 +12,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitor(node: Node): OneOrMany { + function visitor(node: Node): VisitResult { if (node.transformFlags & TransformFlags.ES7) { return visitorWorker(node); } @@ -24,13 +24,15 @@ namespace ts { } } - function visitorWorker(node: Node): OneOrMany { + function visitorWorker(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.BinaryExpression: return visitBinaryExpression(node); - } - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + default: + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); + } } function visitBinaryExpression(node: BinaryExpression): Expression { @@ -90,7 +92,8 @@ namespace ts { return createMathPow(left, right, /*location*/ node); } else { - Debug.fail(`Unexpected operator kind: ${formatSyntaxKind(node.operatorToken.kind)}.`); + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); } } } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 3a68f1f2e28d4..dc423e8da7b15 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -18,7 +18,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitor(node: Node): OneOrMany { + function visitor(node: Node): VisitResult { if (node.transformFlags & TransformFlags.Jsx) { return visitorWorker(node); } @@ -30,16 +30,18 @@ namespace ts { } } - function visitorWorker(node: Node): OneOrMany { + function visitorWorker(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.JsxElement: return visitJsxElement(node); case SyntaxKind.JsxSelfClosingElement: return visitJsxSelfClosingElement(node); - } - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + default: + Debug.failBadSyntaxKind(node); + return undefined; + } } function transformJsxChildToExpression(node: JsxChild): Expression { @@ -55,9 +57,11 @@ namespace ts { case SyntaxKind.JsxSelfClosingElement: return visitJsxSelfClosingElement(node); - } - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + default: + Debug.failBadSyntaxKind(node); + return undefined; + } } function visitJsxElement(node: JsxElement) { diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index a258cb914bb39..bff1396b7a3e4 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -123,7 +123,7 @@ namespace ts { createStatement( createCall( define, - flattenNodes([ + flatten([ // Add the module name (if provided). moduleName, @@ -142,7 +142,7 @@ namespace ts { setNodeEmitFlags( setMultiLine( createBlock( - flattenNodes([ + flatten([ // Visit each statement of the module body. ...visitNodes(node.statements, visitor, isStatement), @@ -173,7 +173,7 @@ namespace ts { * * @param node The node. */ - function visitor(node: Node): OneOrMany { + function visitor(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.ImportDeclaration: return visitImportDeclaration(node); @@ -208,7 +208,7 @@ namespace ts { * * @param node The ImportDeclaration node. */ - function visitImportDeclaration(node: ImportDeclaration): OneOrMany { + function visitImportDeclaration(node: ImportDeclaration): VisitResult { if (contains(externalImports, node)) { const statements: Statement[] = []; const namespaceDeclaration = getNamespaceDeclarationNode(node); @@ -287,7 +287,7 @@ namespace ts { return undefined; } - function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMany { + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult { if (contains(externalImports, node)) { const statements: Statement[] = []; if (moduleKind !== ModuleKind.AMD) { @@ -335,7 +335,7 @@ namespace ts { return undefined; } - function visitExportDeclaration(node: ExportDeclaration): OneOrMany { + function visitExportDeclaration(node: ExportDeclaration): VisitResult { if (contains(externalImports, node)) { const generatedName = getGeneratedNameForNode(node); if (node.exportClause) { @@ -391,7 +391,7 @@ namespace ts { return undefined; } - function visitExportAssignment(node: ExportAssignment): OneOrMany { + function visitExportAssignment(node: ExportAssignment): VisitResult { if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { const statements: Statement[] = []; addExportDefault(statements, node.expression, /*location*/ node); @@ -470,7 +470,7 @@ namespace ts { } } - function visitVariableStatement(node: VariableStatement): OneOrMany { + function visitVariableStatement(node: VariableStatement): VisitResult { const variables = getInitializedVariables(node.declarationList); if (variables.length === 0) { // elide statement if there are no initialized variables @@ -502,7 +502,7 @@ namespace ts { } } - function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMany { + function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { const statements: Statement[] = []; if (node.name) { if (hasModifier(node, ModifierFlags.Export)) { @@ -543,7 +543,7 @@ namespace ts { return statements; } - function visitClassDeclaration(node: ClassDeclaration): OneOrMany { + function visitClassDeclaration(node: ClassDeclaration): VisitResult { const statements: Statement[] = []; if (node.name) { if (hasModifier(node, ModifierFlags.Export)) { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 1ba5d365fcf99..aab231dd2f414 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -408,7 +408,7 @@ namespace ts { return createArrayLiteral(setters); } - function visitSourceElement(node: Node): OneOrMany { + function visitSourceElement(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.ImportDeclaration: return visitImportDeclaration(node); @@ -427,7 +427,7 @@ namespace ts { } } - function visitNestedNode(node: Node): OneOrMany { + function visitNestedNode(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.VariableStatement: return visitVariableStatement(node); @@ -502,7 +502,7 @@ namespace ts { return undefined; } - function visitExportDeclaration(node: ExportDeclaration): OneOrMany { + function visitExportDeclaration(node: ExportDeclaration): VisitResult { if (!node.moduleSpecifier) { const statements: Statement[] = []; addNodes(statements, map(node.exportClause.elements, visitExportSpecifier)); @@ -541,7 +541,7 @@ namespace ts { * * @param node The variable statement to visit. */ - function visitVariableStatement(node: VariableStatement): OneOrMany { + function visitVariableStatement(node: VariableStatement): VisitResult { const isExported = hasModifier(node, ModifierFlags.Export); const expressions: Expression[] = []; for (const variable of node.declarationList.declarations) { @@ -616,7 +616,7 @@ namespace ts { * * @param node The class declaration to visit. */ - function visitClassDeclaration(node: ClassDeclaration): OneOrMany { + function visitClassDeclaration(node: ClassDeclaration): VisitResult { // Hoist the name of the class declaration to the outer module body function. const name = getDeclarationName(node); hoistVariableDeclaration(name); diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index b92e98d7cd499..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) => OneOrMany): OneOrMany { + function visitWithStack(node: Node, visitor: (node: Node) => VisitResult): VisitResult { // Save state const savedCurrentNamespace = currentNamespace; const savedCurrentScope = currentScope; @@ -118,7 +118,7 @@ namespace ts { * * @param node The node to visit. */ - function visitor(node: Node): OneOrMany { + function visitor(node: Node): VisitResult { return visitWithStack(node, visitorWorker); } @@ -127,7 +127,7 @@ namespace ts { * * @param node The node to visit. */ - function visitorWorker(node: Node): OneOrMany { + function visitorWorker(node: Node): VisitResult { if (node.transformFlags & TransformFlags.TypeScript) { // This node is explicitly marked as TypeScript, so we should transform the node. return visitTypeScript(node); @@ -145,7 +145,7 @@ namespace ts { * * @param node The node to visit. */ - function namespaceElementVisitor(node: Node): OneOrMany { + function namespaceElementVisitor(node: Node): VisitResult { return visitWithStack(node, namespaceElementVisitorWorker); } @@ -154,7 +154,7 @@ namespace ts { * * @param node The node to visit. */ - function namespaceElementVisitorWorker(node: Node): OneOrMany { + 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. @@ -173,7 +173,7 @@ namespace ts { * * @param node The node to visit. */ - function classElementVisitor(node: Node): OneOrMany { + 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): OneOrMany { + function classElementVisitorWorker(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.Constructor: // TypeScript constructors are transformed in `transformClassDeclaration`. @@ -202,8 +202,8 @@ namespace ts { return node; default: - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); - break; + Debug.failBadSyntaxKind(node); + return undefined; } } @@ -212,7 +212,7 @@ namespace ts { * * @param node The node to visit. */ - function visitTypeScript(node: Node): OneOrMany { + function visitTypeScript(node: Node): VisitResult { if (hasModifier(node, ModifierFlags.Ambient)) { // TypeScript ambient declarations are elided. return undefined; @@ -373,8 +373,8 @@ namespace ts { return visitImportEqualsDeclaration(node); default: - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); - break; + Debug.failBadSyntaxKind(node); + return undefined; } } @@ -408,7 +408,7 @@ namespace ts { * * @param node The node to transform. */ - function visitClassDeclaration(node: ClassDeclaration): OneOrMany { + function visitClassDeclaration(node: ClassDeclaration): VisitResult { const staticProperties = getInitializedProperties(node, /*isStatic*/ true); const hasExtendsClause = getClassExtendsHeritageClauseElement(node) !== undefined; @@ -1553,7 +1553,7 @@ namespace ts { break; default: - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + Debug.failBadSyntaxKind(node); break; } @@ -1855,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; } @@ -2150,7 +2150,7 @@ namespace ts { * * @param node The enum declaration node. */ - function visitEnumDeclaration(node: EnumDeclaration): OneOrMany { + function visitEnumDeclaration(node: EnumDeclaration): VisitResult { if (shouldElideEnumDeclaration(node)) { return undefined; } @@ -2362,7 +2362,7 @@ namespace ts { * * @param node The module declaration node. */ - function visitModuleDeclaration(node: ModuleDeclaration): OneOrMany { + function visitModuleDeclaration(node: ModuleDeclaration): VisitResult { if (shouldElideModuleDeclaration(node)) { return undefined; } @@ -2481,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; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7e9dafabe7af4..0ca234f482b85 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2735,6 +2735,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, diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 5eec55005faec..ee88d531577cd 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -3,7 +3,7 @@ /* @internal */ namespace ts { - export type OneOrMany = T | T[]; + export type VisitResult = T | T[]; /** * Describes an edge of a Node, used when traversing a syntax tree. @@ -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) => OneOrMany, 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,24 +493,26 @@ 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) => OneOrMany, 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; } + let visitedNode: Node; if (visited === undefined) { - Debug.assert(optional, "Node not optional."); + if (!optional) { + Debug.failNotOptional(); + } + return undefined; } - - let visitedNode: Node; - if (isArray(visited)) { - visitedNode = (lift || extractSingleNode)(>visited); + else if (isArray(visited)) { + visitedNode = (lift || extractSingleNode)(visited); } else { visitedNode = visited; @@ -520,7 +522,7 @@ namespace ts { visitedNode = parenthesize(visitedNode, parentNode); } - Debug.assert(test === undefined || test(visitedNode), "Wrong node type after visit.", () => `Node ${formatSyntaxKind(visitedNode.kind)} did not pass test ${(test).name}.`); + Debug.assertNode(visitedNode, test); aggregateTransformFlags(visitedNode); return visitedNode; } @@ -534,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) => OneOrMany, 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); } @@ -547,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) => OneOrMany, 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; @@ -564,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. @@ -576,18 +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 createNodeArray(updated, nodes, nodes.hasTrailingComma); - } - - return nodes; + return updated || nodes; } /** @@ -597,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) => OneOrMany, context: LexicalEnvironment): T; - export function visitEachChild(node: T & Map, visitor: (node: Node) => OneOrMany, 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; } @@ -657,33 +658,13 @@ namespace ts { return updated; } - /** - * Flattens an array of nodes that could contain NodeArrayNodes. - */ - export function flattenNodes(nodes: OneOrMany[]): T[] { - let result: T[]; - if (nodes) { - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - if (result || node === undefined || isArray(node)) { - if (!result) { - result = nodes.slice(0, i); - } - addNode(result, node); - } - } - } - - return result || nodes; - } - /** * Appends a node to an array. * * @param to The destination array. * @param from The source Node or NodeArrayNode. */ - 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); } @@ -693,18 +674,21 @@ namespace ts { * @param to The destination NodeArray. * @param from The source array of Node or NodeArrayNode. */ - 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 (isArray(from)) { addNodesWorker(to, from, startOnNewLine, test, parenthesize, parentNode, isVisiting); } else { - const node = parenthesize !== undefined ? parenthesize(from, parentNode) : from; - Debug.assert(test === undefined || test(node), "Wrong node type after visit.", () => `Node ${formatSyntaxKind(node.kind)} did not pass test ${(test).name}.`); + const node = parenthesize !== undefined + ? parenthesize(from, parentNode) + : from; + + Debug.assertNode(node, test); if (startOnNewLine) { node.startsOnNewLine = true; @@ -719,7 +703,7 @@ namespace ts { } } - 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); @@ -760,7 +744,7 @@ 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 = getMutableClone(node); mutableNode.statements = mergeStatements(mutableNode.statements, declarations); @@ -776,7 +760,7 @@ 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 = getMutableClone(node); @@ -793,7 +777,7 @@ 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 = getMutableClone(node); @@ -810,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); } @@ -824,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); @@ -846,7 +830,7 @@ 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[]) { + function mergeBlockLexicalEnvironment(node: T, declarations: Statement[]): T { const mutableNode = getMutableClone(node); mutableNode.statements = mergeStatements(node.statements, declarations); return mutableNode; @@ -858,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); } @@ -867,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); } @@ -877,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); } /** @@ -916,11 +900,11 @@ namespace ts { // We do not transform ambient declarations or types, so there is no need to // recursively aggregate transform flags. if (hasModifier(node, ModifierFlags.Ambient) || isTypeNode(node)) { - return 0; + return TransformFlags.None; } // Aggregate the transform flags of each child. - return reduceEachChild(node, aggregateTransformFlagsForChildNode, 0); + return reduceEachChild(node, aggregateTransformFlagsForChildNode, TransformFlags.None); } /** @@ -930,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 From ad0dd4e2e2de21ddd0c6154ddbcf0feca5e56d6f Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 18 Mar 2016 12:38:19 -0700 Subject: [PATCH 41/43] Fixed perf issue in binder, plus PR feedback --- src/compiler/binder.ts | 531 ++++++++++++--------- src/compiler/core.ts | 5 +- src/compiler/transformers/module/module.ts | 319 +++++++------ src/compiler/types.ts | 11 +- src/compiler/utilities.ts | 11 +- src/compiler/visitor.ts | 10 +- 6 files changed, 494 insertions(+), 393 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b5aec17fe669e..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) && !(getModifierFlags(node) & ModifierFlags.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 getModifierFlags(node) & ModifierFlags.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 = getModifierFlags(node) & ModifierFlags.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 (getModifierFlags(declaration) & ModifierFlags.Default) { + if (hasModifier(declaration, ModifierFlags.Default)) { message = Diagnostics.A_module_cannot_have_multiple_default_exports; } }); @@ -862,7 +862,7 @@ namespace ts { } function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { - return getModifierFlags(node) & ModifierFlags.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 (getModifierFlags(node) & ModifierFlags.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 (getModifierFlags(node) & ModifierFlags.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,20 +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) - || isSuperPropertyCall(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: @@ -1944,179 +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; - } - } - - // 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; - } - - break; + return computeParenthesizedExpression(node, subtreeFlags); case SyntaxKind.BinaryExpression: - if (isDestructuringAssignment(node)) { - // Destructuring assignments are ES6 syntax. - transformFlags |= TransformFlags.AssertES6 | TransformFlags.DestructuringAssignment; - } - else if ((node).operatorToken.kind === SyntaxKind.AsteriskAsteriskToken - || (node).operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { - // Exponentiation is ES7 syntax. - transformFlags |= TransformFlags.AssertES7; - } - - break; + 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; + 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 (getModifierFlags(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 ((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 (getModifierFlags(node) & ModifierFlags.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 (getModifierFlags(node) & ModifierFlags.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 - || getModifierFlags(node) & ModifierFlags.Export - || subtreeFlags & TransformFlags.ContainsCapturedLexicalThis - || subtreeFlags & TransformFlags.ContainsDefaultValueAssignments) { - transformFlags |= TransformFlags.AssertES6; - } - - // An async function declaration is TypeScript syntax. - if (getModifierFlags(node) & ModifierFlags.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 (getModifierFlags(node) & ModifierFlags.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: @@ -2124,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; @@ -2161,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. @@ -2174,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) { @@ -2185,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. @@ -2198,13 +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 - || getModifierFlags(node) & (ModifierFlags.Async | ModifierFlags.Abstract) + || hasModifier(node, ModifierFlags.Async | ModifierFlags.Abstract) || (subtreeFlags & TransformFlags.ContainsDecorators && subtreeFlags & TransformFlags.ContainsComputedPropertyName)) { transformFlags |= TransformFlags.AssertTypeScript; @@ -2219,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 (getModifierFlags(node) & ModifierFlags.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; @@ -2230,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; @@ -2239,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/core.ts b/src/compiler/core.ts index c9a0da63cfe73..e4bc42ee45e26 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1013,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; } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index bff1396b7a3e4..d6a89c4ed77c5 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -168,6 +168,26 @@ namespace ts { ]); } + 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. * @@ -209,186 +229,186 @@ namespace ts { * @param node The ImportDeclaration node. */ function visitImportDeclaration(node: ImportDeclaration): VisitResult { - if (contains(externalImports, node)) { - const statements: Statement[] = []; - const namespaceDeclaration = getNamespaceDeclarationNode(node); - if (moduleKind !== ModuleKind.AMD) { - if (!node.importClause) { - // import "mod"; - addNode(statements, - createStatement( - createRequireCall(node), - /*location*/ node + 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 { - const variables: VariableDeclaration[] = []; - if (namespaceDeclaration && !isDefaultImport(node)) { - // import * as n from "mod"; + // 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), - createRequireCall(node) + getGeneratedNameForNode(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), + 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 if (namespaceDeclaration && isDefaultImport(node)) { - // import d, * as n from "mod"; + else { addNode(statements, createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList([ createVariableDeclaration( - getSynthesizedClone(namespaceDeclaration.name), - getGeneratedNameForNode(node), + getSynthesizedClone(node.name), + createRequireCall(node), /*location*/ node ) ]) ) ); } - - addExportImportAssignments(statements, node); - return statements; + } + else { + if (hasModifier(node, ModifierFlags.Export)) { + addNode(statements, + createStatement( + createExportAssignment(node.name, node.name), + /*location*/ node + ) + ); + } } - return undefined; + addExportImportAssignments(statements, node); + return statements; } - function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult { - if (contains(externalImports, node)) { + 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) { - 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 - ) - ]) - ) - ); - } + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + generatedName, + createRequireCall(node), + /*location*/ node + ) + ]) + ) + ); } - else { - if (hasModifier(node, ModifierFlags.Export)) { + for (const specifier of node.exportClause.elements) { + if (resolver.isValueAliasDeclaration(specifier)) { + const exportedValue = createPropertyAccess( + generatedName, + specifier.propertyName || specifier.name + ); addNode(statements, createStatement( - createExportAssignment(node.name, node.name), - /*location*/ node + createExportAssignment(specifier.name, exportedValue), + /*location*/ specifier ) ); } } - addExportImportAssignments(statements, node); return statements; } - - return undefined; - } - - function visitExportDeclaration(node: ExportDeclaration): VisitResult { - if (contains(externalImports, node)) { - 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 - ); - } + else { + // export * from "mod"; + return createStatement( + createCall( + createIdentifier("__export"), + [ + moduleKind !== ModuleKind.AMD + ? createRequireCall(node) + : generatedName + ] + ), + /*location*/ node + ); } - - return undefined; } function visitExportAssignment(node: ExportAssignment): VisitResult { @@ -605,26 +625,6 @@ namespace ts { return node; } - 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; - } - function getModuleMemberName(name: Identifier) { return createPropertyAccess( createIdentifier("exports"), @@ -669,9 +669,10 @@ namespace ts { function createRequireCall(importNode: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { const moduleName = getExternalModuleNameLiteral(importNode); + Debug.assert(isDefined(moduleName)); return createCall( createIdentifier("require"), - moduleName ? [moduleName] : [] + [moduleName] ); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0ca234f482b85..38d1687acead7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -419,7 +419,9 @@ namespace ts { 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 + Const = 1 << 11, // Variable declaration + + HasComputedFlags = 1 << 31, // Modifier flags have been computed AccessibilityModifier = Public | Private | Protected, NonPublicAccessibilityModifier = Private | Protected, @@ -450,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?: NodeArray; // 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. @@ -2761,6 +2764,8 @@ namespace ts { 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. AssertTypeScript = TypeScript | ContainsTypeScript, @@ -2771,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 | DestructuringAssignment, + 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, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ca8e4d5289e8c..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; } @@ -2561,19 +2561,26 @@ namespace ts { } export function hasModifier(node: Node, flags: ModifierFlags) { - return (getModifierFlags(node) & flags) != 0; + 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; } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index ee88d531577cd..051c020558a34 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -882,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; } /** From 44ca7d435fc9b0d9b6f05de360dd11a4a18726a4 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 18 Mar 2016 13:04:56 -0700 Subject: [PATCH 42/43] Updated baselines --- .../DocComments.parsesCorrectly.leadingAsterisk.json | 3 +-- .../DocComments.parsesCorrectly.noLeadingAsterisk.json | 3 +-- .../DocComments.parsesCorrectly.noReturnType.json | 3 +-- .../JSDocParsing/DocComments.parsesCorrectly.noType.json | 3 +-- .../DocComments.parsesCorrectly.oneParamTag.json | 3 +-- .../JSDocParsing/DocComments.parsesCorrectly.paramTag1.json | 3 +-- .../DocComments.parsesCorrectly.paramTagBracketedName1.json | 3 +-- .../DocComments.parsesCorrectly.paramTagBracketedName2.json | 3 +-- .../DocComments.parsesCorrectly.paramTagNameThenType1.json | 3 +-- .../DocComments.parsesCorrectly.paramTagNameThenType2.json | 3 +-- .../DocComments.parsesCorrectly.paramWithoutType.json | 3 +-- .../DocComments.parsesCorrectly.returnTag1.json | 3 +-- .../DocComments.parsesCorrectly.returnTag2.json | 3 +-- .../DocComments.parsesCorrectly.returnsTag1.json | 3 +-- .../DocComments.parsesCorrectly.templateTag.json | 6 ++---- .../DocComments.parsesCorrectly.templateTag2.json | 6 ++---- .../DocComments.parsesCorrectly.templateTag3.json | 6 ++---- .../DocComments.parsesCorrectly.templateTag4.json | 6 ++---- .../DocComments.parsesCorrectly.templateTag5.json | 6 ++---- .../DocComments.parsesCorrectly.templateTag6.json | 6 ++---- .../DocComments.parsesCorrectly.twoParamTag2.json | 3 +-- .../DocComments.parsesCorrectly.twoParamTagOnSameLine.json | 3 +-- .../JSDocParsing/DocComments.parsesCorrectly.typeTag.json | 3 +-- ...TypeExpressions.parsesCorrectly.functionReturnType1.json | 3 +-- .../TypeExpressions.parsesCorrectly.functionType1.json | 3 +-- .../TypeExpressions.parsesCorrectly.functionType2.json | 3 +-- .../TypeExpressions.parsesCorrectly.recordType1.json | 3 +-- .../TypeExpressions.parsesCorrectly.recordType2.json | 3 +-- .../TypeExpressions.parsesCorrectly.recordType3.json | 3 +-- .../TypeExpressions.parsesCorrectly.recordType4.json | 3 +-- .../TypeExpressions.parsesCorrectly.recordType5.json | 3 +-- .../TypeExpressions.parsesCorrectly.recordType6.json | 3 +-- .../TypeExpressions.parsesCorrectly.recordType7.json | 3 +-- .../TypeExpressions.parsesCorrectly.recordType8.json | 3 +-- ...xpressions.parsesCorrectly.topLevelNoParenUnionType.json | 3 +-- .../TypeExpressions.parsesCorrectly.tupleType0.json | 3 +-- .../TypeExpressions.parsesCorrectly.tupleType1.json | 3 +-- .../TypeExpressions.parsesCorrectly.tupleType2.json | 3 +-- .../TypeExpressions.parsesCorrectly.tupleType3.json | 3 +-- .../TypeExpressions.parsesCorrectly.typeReference1.json | 3 +-- .../TypeExpressions.parsesCorrectly.typeReference2.json | 3 +-- .../TypeExpressions.parsesCorrectly.unionType.json | 3 +-- 42 files changed, 48 insertions(+), 96 deletions(-) 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 From ae7843dd952d8f1b6b88ab63ff792b4bece45415 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 18 Mar 2016 13:05:59 -0700 Subject: [PATCH 43/43] PR Feedback --- src/compiler/printer.ts | 2 +- src/compiler/transformers/es6.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index eb1bb2a658188..14c08eeab4512 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -1987,7 +1987,7 @@ const _super = (function (geti, seti) { if (substitution && (getNodeEmitFlags(node) & NodeEmitFlags.NoSubstitution) === 0) { const substitute = substitution(node); if (substitute !== node) { - setNodeEmitFlags(substitute, NodeEmitFlags.NoSubstitution); + setNodeEmitFlags(substitute, NodeEmitFlags.NoSubstitution | getNodeEmitFlags(substitute)); emitWorker(substitute); return true; } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index ebc50ad7b1676..983d33ba73ed9 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -203,7 +203,7 @@ namespace ts { /** * Visits a ClassDeclaration and transforms it into a variable statement. * - * @parma node A ClassDeclaration node. + * @param node A ClassDeclaration node. */ function visitClassDeclaration(node: ClassDeclaration): Statement { // [source]