From ad64877dee83fd63a7b63a2789f8ca1d95717571 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 15:15:09 -0800 Subject: [PATCH] Merged some changes from other branches. --- src/compiler/core.ts | 104 +++++++++++++++++++++++++++++--------- src/compiler/emitter.ts | 2 +- src/compiler/factory.ts | 27 +++++++--- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 6 --- src/compiler/visitor.ts | 76 +++++++++++++++++++--------- 6 files changed, 155 insertions(+), 61 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index c7f05f4cf5..3d82c0efe1 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -130,11 +130,12 @@ namespace ts { return -1; } - export function countWhere(array: T[], predicate: (x: T) => boolean): number { + export function countWhere(array: T[], predicate: (x: T, i: number) => boolean): number { let count = 0; if (array) { - for (const v of array) { - if (predicate(v)) { + for (let i = 0; i < array.length; i++) { + const v = array[i]; + if (predicate(v, i)) { count++; } } @@ -142,25 +143,49 @@ namespace ts { return count; } - export function filter(array: T[], f: (x: T) => boolean): T[] { + export function filter(array: T[], f: (x: T, i: number) => x is U): U[]; + export function filter(array: T[], f: (x: T, i: number) => boolean): T[]; + export function filter(array: T[], f: (x: T, i: number) => boolean): T[] { let result: T[]; if (array) { result = []; - for (const item of array) { - if (f(item)) { - result.push(item); + for (let i = 0; i < array.length; i++) { + const v = array[i]; + if (f(v, i)) { + result.push(v); } } } return result; } - export function map(array: T[], f: (x: T) => U): U[] { + export function map(array: T[], f: (x: T, i: number) => U): U[] { let result: U[]; if (array) { result = []; - for (const v of array) { - result.push(f(v)); + for (let i = 0; i < array.length; i++) { + const v = array[i]; + result.push(f(v, i)); + } + } + return result; + } + + /** + * Maps an array. If the mapped value is an array, it is spread into the result. + */ + export function flatMap(array: T[], f: (x: T, i: number) => U | U[]): U[] { + let result: U[]; + if (array) { + result = []; + for (let i = 0; i < array.length; i++) { + const v = array[i]; + const ar = f(v, i); + if (ar) { + // We cast to here to leverage the behavior of Array#concat + // which will append a single value here. + result = result.concat(ar); + } } } return result; @@ -170,7 +195,7 @@ namespace ts { if (!array2 || !array2.length) return array1; if (!array1 || !array1.length) return array2; - return array1.concat(array2); + return [...array1, ...array2]; } export function deduplicate(array: T[]): T[] { @@ -186,6 +211,27 @@ namespace ts { return result; } + /** + * Compacts an array, removing any falsey elements. + */ + export function compact(array: T[]): T[] { + let result: T[]; + if (array) { + for (let i = 0; i < array.length; i++) { + const v = array[i]; + if (result || !v) { + if (!result) { + result = array.slice(0, i); + } + if (v) { + result.push(v); + } + } + } + } + return result || array; + } + export function sum(array: any[], prop: string): number { let result = 0; for (const v of array) { @@ -212,15 +258,25 @@ namespace ts { return true; } + export function firstOrUndefined(array: T[]): T { + return array && array.length > 0 + ? array[0] + : undefined; + } + + export function singleOrUndefined(array: T[]): T { + return array && array.length === 1 + ? array[0] + : undefined; + } + /** * Returns the last element of an array if non-empty, undefined otherwise. */ export function lastOrUndefined(array: T[]): T { - if (array.length === 0) { - return undefined; - } - - return array[array.length - 1]; + return array && array.length > 0 + ? array[array.length - 1] + : undefined; } /** @@ -252,9 +308,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) { @@ -268,7 +324,7 @@ namespace ts { result = initial; } while (pos < count) { - result = f(result, array[pos]); + result = f(result, array[pos], pos); pos++; } return result; @@ -277,9 +333,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) { @@ -292,7 +348,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/emitter.ts b/src/compiler/emitter.ts index f0e32bdf96..37a278aee7 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2728,7 +2728,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) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index c41182d4e2..f39076da24 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -22,8 +22,8 @@ namespace ts { return node; } - export function createNodeArray(elements?: T[], location?: TextRange): NodeArray { - if (elements !== undefined) { + export function createNodeArray(elements?: T[], location?: TextRange, hasTrailingComma?: boolean): NodeArray { + if (elements) { if (isNodeArray(elements)) { return elements; } @@ -33,7 +33,7 @@ namespace ts { } const array = >elements; - if (location !== undefined) { + if (location) { array.pos = location.pos; array.end = location.end; } @@ -42,13 +42,17 @@ namespace ts { array.end = -1; } + if (hasTrailingComma) { + array.hasTrailingComma = true; + } + array.arrayKind = ArrayKind.NodeArray; return array; } export function createModifiersArray(elements?: Modifier[], location?: TextRange): ModifiersArray { let flags: NodeFlags; - if (elements !== undefined) { + if (elements) { if (isModifiersArray(elements)) { return elements; } @@ -64,7 +68,7 @@ namespace ts { } const array = elements; - if (location !== undefined) { + if (location) { array.pos = location.pos; array.end = location.end; } @@ -79,7 +83,7 @@ namespace ts { } export function setModifiers(node: T, modifiers: Modifier[]) { - if (modifiers !== undefined) { + if (modifiers) { const array = createModifiersArray(modifiers); node.modifiers = array; node.flags |= array.flags; @@ -92,7 +96,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; } @@ -145,6 +149,15 @@ namespace ts { return clone; } + /** + * Creates a shallow, memberwise clone of a node for mutation. + * + * @param node The node to clone. + */ + 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); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6e4f1b35d6..95656cea08 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -447,6 +447,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) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1cef5e8835..b98e43f1a3 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -8,12 +8,6 @@ namespace ts { isNoDefaultLib?: boolean; } - export interface SynthesizedNode extends Node { - leadingCommentRanges?: CommentRange[]; - trailingCommentRanges?: CommentRange[]; - startsOnNewLine: boolean; - } - export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration { const declarations = symbol.declarations; if (declarations) { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 0c12eaa9be..6031e39884 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. @@ -528,21 +528,21 @@ 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. updated = nodes.slice(0, i); } - addNode(updated, visited, test); + addNodeWorker(updated, visited, /*addOnNewLine*/ undefined, test); } } if (updated !== undefined) { return (isModifiersArray(nodes) ? createModifiersArray(updated, nodes) - : createNodeArray(updated, nodes)); + : createNodeArray(updated, nodes, nodes.hasTrailingComma)); } return nodes; @@ -571,21 +571,28 @@ 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); + updated = getMutableNode(node); + updated.flags &= ~NodeFlags.Modifier; } - 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); } } } @@ -603,9 +610,20 @@ namespace ts { } } + if (updated !== node) { + updated.original = node; + } + 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) { + parentNode[edge.name] = value; + } + /** * Visits a node edge. * @@ -626,16 +644,8 @@ namespace ts { * @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) { - if (to !== undefined && from !== undefined) { - if (isNodeArrayNode(from)) { - addNodes(to, from.nodes, test); - } - else { - Debug.assert(test === undefined || test(from), "Wrong node type after visit."); - to.push(from); - } - } + export function addNode(to: T[], from: OneOrMany, startOnNewLine?: boolean) { + addNodeWorker(to, from, startOnNewLine, /*test*/ undefined); } /** @@ -645,10 +655,30 @@ namespace ts { * @param from The source array of Node or NodeArrayNode. * @param test The node test used to validate each node. */ - export function addNodes(to: T[], from: OneOrMore[], test?: (node: Node) => boolean) { - if (to !== undefined && from !== undefined) { + export function addNodes(to: T[], from: OneOrMany[], startOnNewLine?: boolean) { + addNodesWorker(to, from, startOnNewLine, /*test*/ undefined); + } + + function addNodeWorker(to: T[], from: OneOrMany, startOnNewLine: boolean, test: (node: Node) => boolean) { + if (to && from) { + if (isNodeArrayNode(from)) { + addNodesWorker(to, from.nodes, startOnNewLine, test); + } + else { + Debug.assert(test === undefined || test(from), "Wrong node type after visit."); + if (startOnNewLine) { + from.startsOnNewLine = true; + } + + to.push(from); + } + } + } + + function addNodesWorker(to: T[], from: OneOrMany[], startOnNewLine: boolean, test: (node: Node) => boolean) { + if (to && from) { for (const node of from) { - addNode(to, node, test); + addNodeWorker(to, node, startOnNewLine, test); } } }