From dd2dc78dd6d1ee723beddc5e62e0818ca6452ffd Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 19 Feb 2016 16:59:57 -0800 Subject: [PATCH 01/19] 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 02/19] 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 03/19] 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 04/19] 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 72bfd2f5b6a8467c52a4e99e2fc27bab04cc532c Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 23 Feb 2016 17:06:54 -0800 Subject: [PATCH 05/19] 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 30433c2c67cf8073b087e8ac90e36d1cf3a61c6b Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 2 Mar 2016 11:38:30 -0800 Subject: [PATCH 06/19] 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 07/19] 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 1c738181efcd1e84054e78ea7494a9f30aef429e Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 2 Mar 2016 15:47:28 -0800 Subject: [PATCH 08/19] 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 09/19] 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 10/19] 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 11/19] 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 12/19] 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 13/19] 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 14/19] 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 15/19] 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 16/19] 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 17/19] 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 18/19] 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 19/19] 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]