From 49d2d933796c2a8bdfa55d5e2830a97b3766c9a8 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 9 Feb 2016 15:27:33 -0800 Subject: [PATCH] Adds destructuring logic and placeholders for each transformer. --- src/compiler/factory.ts | 33 +- src/compiler/sourcemap.ts | 37 ++- src/compiler/transformer.ts | 36 ++- src/compiler/transformers/destructuring.ts | 347 +++++++++++++++++++++ src/compiler/transformers/es6.ts | 30 ++ src/compiler/transformers/es7.ts | 32 ++ src/compiler/transformers/jsx.ts | 30 ++ src/compiler/transformers/module/es6.ts | 17 + src/compiler/transformers/module/module.ts | 18 ++ src/compiler/transformers/module/system.ts | 18 ++ src/compiler/transformers/ts.ts | 30 ++ src/compiler/types.ts | 4 +- src/compiler/utilities.ts | 65 +++- 13 files changed, 682 insertions(+), 15 deletions(-) create mode 100644 src/compiler/transformers/destructuring.ts create mode 100644 src/compiler/transformers/es6.ts create mode 100644 src/compiler/transformers/es7.ts create mode 100644 src/compiler/transformers/jsx.ts create mode 100644 src/compiler/transformers/module/es6.ts create mode 100644 src/compiler/transformers/module/module.ts create mode 100644 src/compiler/transformers/module/system.ts create mode 100644 src/compiler/transformers/ts.ts diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 0d225184a4b..242c67f51b4 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -135,9 +135,18 @@ namespace ts { return node; } - export function createTempVariable(tempKind: TempVariableKind): Identifier { + export function createTempVariable(): Identifier { const name = createNode(SyntaxKind.Identifier); - name.tempKind = tempKind; + name.text = undefined; + name.tempKind = TempVariableKind.Auto; + getNodeId(name); + return name; + } + + export function createLoopVariable(): Identifier { + const name = createNode(SyntaxKind.Identifier); + name.text = undefined; + name.tempKind = TempVariableKind.Loop; getNodeId(name); return name; } @@ -171,16 +180,16 @@ namespace ts { return createVoid(createLiteral(0)); } - export function createPropertyAccess(expression: Expression, name: string | Identifier) { - const node = createNode(SyntaxKind.PropertyAccessExpression); + export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange) { + const node = createNode(SyntaxKind.PropertyAccessExpression, location); node.expression = parenthesizeForAccess(expression); node.dotToken = createSynthesizedNode(SyntaxKind.DotToken); node.name = coerceIdentifier(name); return node; } - export function createElementAccess(expression: Expression, index: string | number | Expression) { - const node = createNode(SyntaxKind.ElementAccessExpression); + export function createElementAccess(expression: Expression, index: string | number | Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ElementAccessExpression, location); node.expression = parenthesizeForAccess(expression); node.argumentExpression = coerceExpression(index); return node; @@ -216,8 +225,8 @@ namespace ts { return createBinary(left, SyntaxKind.CommaToken, right); } - export function createCall(expression: Expression, argumentsArray: Expression[]) { - const node = createNode(SyntaxKind.CallExpression); + export function createCall(expression: Expression, argumentsArray: Expression[], location?: TextRange) { + const node = createNode(SyntaxKind.CallExpression, location); node.expression = parenthesizeForAccess(expression); node.arguments = createNodeArray(argumentsArray); return node; @@ -228,6 +237,14 @@ namespace ts { return createCall(createPropertyAccess(array, "slice"), argumentsList); } + export function createMathPow(left: Expression, right: Expression, location?: TextRange) { + return createCall( + createPropertyAccess(createIdentifier("Math"), "pow"), + [left, right], + location + ); + } + export function parenthesizeExpression(expression: Expression) { const node = createNode(SyntaxKind.ParenthesizedExpression); node.expression = expression; diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 86bad38cb6e..5e453b1b101 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -7,12 +7,15 @@ namespace ts { setSourceFile(sourceFile: SourceFile): void; emitPos(pos: number): void; emitStart(range: TextRange): void; - emitEnd(range: TextRange, stopOverridingSpan?: boolean): void; - changeEmitSourcePos(): void; + emitEnd(range: TextRange): void; + /*@deprecated*/ emitEnd(range: TextRange, stopOverridingSpan: boolean): void; + /*@deprecated*/ changeEmitSourcePos(): void; getText(): string; getSourceMappingURL(): string; initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void; reset(): void; + enable(): void; + disable(): void; } let nullSourceMapWriter: SourceMapWriter; @@ -38,6 +41,8 @@ namespace ts { getSourceMappingURL(): string { return undefined; }, initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void { }, reset(): void { }, + enable(): void { }, + disable(): void { } }; } @@ -62,6 +67,8 @@ namespace ts { // Source map data let sourceMapData: SourceMapData; + let disableDepth: number; + return { getSourceMapData: () => sourceMapData, setSourceFile, @@ -73,6 +80,8 @@ namespace ts { getSourceMappingURL, initialize, reset, + enable, + disable, }; function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { @@ -81,6 +90,7 @@ namespace ts { } currentSourceFile = undefined; + disableDepth = 0; // Current source map file and its index in the sources list sourceMapSourceIndex = -1; @@ -147,6 +157,17 @@ namespace ts { lastEncodedSourceMapSpan = undefined; lastEncodedNameIndex = undefined; sourceMapData = undefined; + disableDepth = 0; + } + + function enable() { + if (disableDepth > 0) { + disableDepth--; + } + } + + function disable() { + disableDepth++; } function updateLastEncodedAndRecordedSpans() { @@ -168,7 +189,7 @@ namespace ts { sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] : defaultLastEncodedSourceMapSpan; - // TODO: Update lastEncodedNameIndex + // TODO: Update lastEncodedNameIndex // Since we dont support this any more, lets not worry about it right now. // When we start supporting nameIndex, we will get back to this @@ -236,7 +257,7 @@ namespace ts { } function emitPos(pos: number) { - if (pos === -1) { + if (positionIsSynthesized(pos) || disableDepth > 0) { return; } @@ -288,9 +309,17 @@ namespace ts { function emitStart(range: TextRange) { emitPos(getStartPos(range)); + + if ((range).disableSourceMap) { + disable(); + } } function emitEnd(range: TextRange, stopOverridingEnd?: boolean) { + if ((range).disableSourceMap) { + enable(); + } + emitPos(range.end); stopOverridingSpan = stopOverridingEnd; } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 0507442642e..3e6e1dcf6e5 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -1,10 +1,44 @@ /// +/// +/// +/// +/// +/// +/// +/// + /* @internal */ namespace ts { + const moduleTransformerMap: Map = { + [ModuleKind.ES6]: transformES6Module, + [ModuleKind.System]: transformSystemModule, + [ModuleKind.AMD]: transformModule, + [ModuleKind.CommonJS]: transformModule, + [ModuleKind.UMD]: transformModule, + [ModuleKind.None]: transformModule + }; + export function getTransformers(compilerOptions: CompilerOptions) { + const jsx = compilerOptions.jsx; + const languageVersion = getLanguageVersion(compilerOptions); + const moduleKind = getModuleKind(compilerOptions); const transformers: Transformer[] = []; - // TODO(rbuckton): Add transformers + + transformers.push(transformTypeScript); + transformers.push(moduleTransformerMap[moduleKind]); + if (jsx === JsxEmit.React) { + transformers.push(transformJsx); + } + + if (languageVersion < ScriptTarget.ES7) { + transformers.push(transformES7); + } + + if (languageVersion < ScriptTarget.ES6) { + transformers.push(transformES6); + } + return transformers; } diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts new file mode 100644 index 00000000000..19c25e5699f --- /dev/null +++ b/src/compiler/transformers/destructuring.ts @@ -0,0 +1,347 @@ +/// +/// + +/*@internal*/ +namespace ts { + /** + * Flattens a destructuring assignment expression. + * + * @param root The destructuring assignment expression. + * @param needsValue Indicates whether the value from the right-hand-side of the + * destructuring assignment is needed as part of a larger expression. + * @param recordTempVariable A callback used to record new temporary variables. + */ + export function flattenDestructuringAssignment(node: BinaryExpression, needsValue: boolean, recordTempVariable: (node: Identifier) => void) { + let location: TextRange = node; + let value = node.right; + if (isEmptyObjectLiteralOrArrayLiteral(node.left)) { + return value; + } + + const expressions: Expression[] = []; + if (needsValue) { + // Temporary assignment needed to emit root should highlight whole binary expression + value = ensureIdentifier(node.right, /*reuseIdentifierExpressions*/ true, node, emitTempVariableAssignment); + } + else if (nodeIsSynthesized(node)) { + // Source map node for root.left = root.right is root + // but if root is synthetic, which could be in below case, use the target which is { a } + // for ({a} of {a: string}) { + // } + location = node.right; + } + + flattenDestructuring(node, value, location, emitAssignment, emitTempVariableAssignment); + + if (needsValue) { + expressions.push(value); + } + + const expression = inlineExpressions(expressions); + aggregateTransformFlags(expression); + return expression; + + function emitAssignment(name: Identifier, value: Expression, location: TextRange) { + const expression = createAssignment(name, value, location); + if (isSimpleExpression(value)) { + (expression).disableSourceMap = true; + } + + aggregateTransformFlags(expression); + expressions.push(expression); + } + + function emitTempVariableAssignment(value: Expression, location: TextRange) { + const name = createTempVariable(); + recordTempVariable(name); + emitAssignment(name, value, location); + return name; + } + } + + /** + * Flattens binding patterns in a parameter declaration. + * + * @param node The ParameterDeclaration to flatten. + * @param value The rhs value for the binding pattern. + */ + export function flattenParameterDestructuring(node: ParameterDeclaration, value: Expression) { + const declarations: VariableDeclaration[] = []; + + flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment); + + return declarations; + + function emitAssignment(name: Identifier, value: Expression, location: TextRange) { + const declaration = createVariableDeclaration(name, value, location); + if (isSimpleExpression(value)) { + (declaration).disableSourceMap = true; + } + + aggregateTransformFlags(declaration); + declarations.push(declaration); + } + + function emitTempVariableAssignment(value: Expression, location: TextRange) { + const name = createTempVariable(); + emitAssignment(name, value, location); + return name; + } + } + + /** + * Flattens binding patterns in a variable declaration. + * + * @param node The VariableDeclaration to flatten. + * @param value An optional rhs value for the binding pattern. + */ + export function flattenVariableDestructuring(node: VariableDeclaration, value?: Expression) { + const declarations: VariableDeclaration[] = []; + + flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment); + + return declarations; + + function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) { + const declaration = createVariableDeclaration(name, value, location); + if (declarations.length === 0) { + declaration.pos = -1; + } + + if (isSimpleExpression(value)) { + (declaration).disableSourceMap = true; + } + + declaration.original = original; + declarations.push(declaration); + aggregateTransformFlags(declaration); + } + + function emitTempVariableAssignment(value: Expression, location: TextRange) { + const name = createTempVariable(); + emitAssignment(name, value, location, /*original*/ undefined); + return name; + } + } + + /** + * Flattens binding patterns in a variable declaration and transforms them into an expression. + * + * @param node The VariableDeclaration to flatten. + * @param recordTempVariable A callback used to record new temporary variables. + */ + export function flattenVariableDestructuringToExpression(node: VariableDeclaration, recordTempVariable: (name: Identifier) => void) { + const pendingAssignments: Expression[] = []; + + flattenDestructuring(node, /*value*/ undefined, node, emitAssignment, emitTempVariableAssignment); + + const expression = inlineExpressions(pendingAssignments); + aggregateTransformFlags(expression); + return expression; + + function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) { + const expression = createAssignment(name, value, location); + if (isSimpleExpression(value)) { + (expression).disableSourceMap = true; + } + + expression.original = original; + pendingAssignments.push(expression); + } + + function emitTempVariableAssignment(value: Expression, location: TextRange) { + const name = createTempVariable(); + recordTempVariable(name); + emitAssignment(name, value, location, /*original*/ undefined); + return name; + } + } + + function flattenDestructuring( + root: BindingElement | BinaryExpression, + value: Expression, + location: TextRange, + emitAssignment: (name: Identifier, value: Expression, location: TextRange, original: Node) => void, + emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier) { + if (isBinaryExpression(root)) { + emitDestructuringAssignment(root.left, value, location) + } + else { + emitBindingElement(root, value); + } + + function emitDestructuringAssignment(bindingTarget: Expression | ShorthandPropertyAssignment, value: Expression, location: TextRange) { + // When emitting target = value use source map node to highlight, including any temporary assignments needed for this + let target: Expression; + if (isShortHandPropertyAssignment(bindingTarget)) { + if (bindingTarget.objectAssignmentInitializer) { + value = createDefaultValueCheck(value, bindingTarget.objectAssignmentInitializer, location); + } + + target = bindingTarget.name; + } + else if (isBinaryExpression(bindingTarget) && bindingTarget.operatorToken.kind === SyntaxKind.EqualsToken) { + value = createDefaultValueCheck(value, bindingTarget.right, location); + target = bindingTarget.left; + } + else { + target = bindingTarget; + } + + if (target.kind === SyntaxKind.ObjectLiteralExpression) { + emitObjectLiteralAssignment(target, value, location); + } + else if (target.kind === SyntaxKind.ArrayLiteralExpression) { + emitArrayLiteralAssignment(target, value, location); + } + else { + const name = cloneNode(target, /*location*/ target, /*flags*/ undefined, /*parent*/ undefined, /*original*/ target); + emitAssignment(name, value, location, /*original*/ undefined); + } + } + + function emitObjectLiteralAssignment(target: ObjectLiteralExpression, value: Expression, location: TextRange) { + const properties = target.properties; + if (properties.length !== 1) { + // For anything but a single element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. + // When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); + } + + for (const p of properties) { + if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) { + const propName = (p).name; + const target = p.kind === SyntaxKind.ShorthandPropertyAssignment ? p : (p).initializer || propName; + // Assignment for target = value.propName should highligh whole property, hence use p as source map node + emitDestructuringAssignment(target, createDestructuringPropertyAccess(value, propName), p); + } + } + } + + function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) { + const elements = target.elements; + if (elements.length !== 1) { + // For anything but a single element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. + // When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); + } + + for (let i = 0; i < elements.length; i++) { + const e = elements[i]; + if (e.kind !== SyntaxKind.OmittedExpression) { + // Assignment for target = value.propName should highligh whole property, hence use e as source map node + if (e.kind !== SyntaxKind.SpreadElementExpression) { + emitDestructuringAssignment(e, createElementAccess(value, createLiteral(i)), e); + } + else if (i === elements.length - 1) { + emitDestructuringAssignment((e).expression, createArraySlice(value, i), e); + } + } + } + } + + function emitBindingElement(target: BindingElement, value: Expression) { + // Any temporary assignments needed to emit target = value should point to target + if (target.initializer) { + // Combine value and initializer + value = value ? createDefaultValueCheck(value, target.initializer, target) : target.initializer; + } + else if (!value) { + // Use 'void 0' in absence of value and initializer + value = createVoidZero(); + } + + const name = target.name; + if (isBindingPattern(name)) { + const elements = name.elements; + const numElements = elements.length; + if (numElements !== 1) { + // For anything other than a single-element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. Additionally, if we have zero elements + // we need to emit *something* to ensure that in case a 'var' keyword was already emitted, + // so in that case, we'll intentionally create that temporary. + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0, target, emitTempVariableAssignment); + } + for (let i = 0; i < elements.length; i++) { + let element = elements[i]; + if (name.kind === SyntaxKind.ObjectBindingPattern) { + // Rewrite element to a declaration with an initializer that fetches property + let propName = element.propertyName || element.name; + emitBindingElement(element, createDestructuringPropertyAccess(value, propName)); + } + else if (element.kind !== SyntaxKind.OmittedExpression) { + if (!element.dotDotDotToken) { + // Rewrite element to a declaration that accesses array element at index i + emitBindingElement(element, createElementAccess(value, i)); + } + else if (i === elements.length - 1) { + emitBindingElement(element, createArraySlice(value, i)); + } + } + } + } + else { + const clonedName = cloneNode(name, /*location*/ undefined, /*flags*/ undefined, /*parent*/ undefined, /*original*/ name); + emitAssignment(clonedName, value, target, target); + } + } + + function createDefaultValueCheck(value: Expression, defaultValue: Expression, location: TextRange): Expression { + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); + return createConditional( + createStrictEquality(value, createVoidZero()), + defaultValue, + value + ); + } + + function createDestructuringPropertyAccess(object: Expression, propertyName: PropertyName): LeftHandSideExpression { + if (isComputedPropertyName(propertyName)) { + return createElementAccess( + object, + ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, propertyName, emitTempVariableAssignment) + ); + } + else if (isIdentifier(propertyName)) { + return createPropertyAccess( + object, + propertyName.text + ); + } + else { + // We create a synthetic copy of the identifier in order to avoid the rewriting that might + // otherwise occur when the identifier is emitted. + return createElementAccess( + object, + cloneNode(propertyName) + ); + } + } + } + + /** + * Ensures that there exists a declared identifier whose value holds the given expression. + * This function is useful to ensure that the expression's value can be read from in subsequent expressions. + * Unless 'reuseIdentifierExpressions' is false, 'value' will be returned if it is just an identifier. + * + * @param value the expression whose value needs to be bound. + * @param reuseIdentifierExpressions true if identifier expressions can simply be returned; + * false if it is necessary to always emit an identifier. + * @param location The location to use for source maps and comments. + * @param emitTempVariableAssignment A callback used to emit a temporary variable. + */ + function ensureIdentifier( + value: Expression, + reuseIdentifierExpressions: boolean, + location: TextRange, + emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier) { + if (isIdentifier(value) && reuseIdentifierExpressions) { + return value; + } + else { + return emitTempVariableAssignment(value, location); + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts new file mode 100644 index 00000000000..d5c5f9566db --- /dev/null +++ b/src/compiler/transformers/es6.ts @@ -0,0 +1,30 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): ES6->ES5 transformer + export function transformES6(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + if (node.transformFlags & TransformFlags.ES6) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsES6) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts new file mode 100644 index 00000000000..e4d1e564cb8 --- /dev/null +++ b/src/compiler/transformers/es7.ts @@ -0,0 +1,32 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): ES7->ES6 transformer + export function transformES7(context: TransformationContext) { + const { hoistVariableDeclaration } = context; + + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + if (node.transformFlags & TransformFlags.ES7) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsES7) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node) { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts new file mode 100644 index 00000000000..e9470b32eea --- /dev/null +++ b/src/compiler/transformers/jsx.ts @@ -0,0 +1,30 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): JSX->React transformer + export function transformJsx(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + if (node.transformFlags & TransformFlags.Jsx) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsJsx) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/module/es6.ts b/src/compiler/transformers/module/es6.ts new file mode 100644 index 00000000000..b49d0a5c83b --- /dev/null +++ b/src/compiler/transformers/module/es6.ts @@ -0,0 +1,17 @@ +/// +/// + +/*@internal*/ +namespace ts { + export function transformES6Module(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts new file mode 100644 index 00000000000..61c4a2cd880 --- /dev/null +++ b/src/compiler/transformers/module/module.ts @@ -0,0 +1,18 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): CommonJS/AMD/UMD transformer + export function transformModule(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts new file mode 100644 index 00000000000..e4c03d32b45 --- /dev/null +++ b/src/compiler/transformers/module/system.ts @@ -0,0 +1,18 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): System module transformer + export function transformSystemModule(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts new file mode 100644 index 00000000000..0156bb5ff84 --- /dev/null +++ b/src/compiler/transformers/ts.ts @@ -0,0 +1,30 @@ +/// +/// + +/*@internal*/ +namespace ts { + // TODO(rbuckton): TS->ES7 transformer + export function transformTypeScript(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node) { + if (node.transformFlags & TransformFlags.TypeScript) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsTypeScript) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node): Node { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 70338d3d0f2..6cd3a666788 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2508,8 +2508,10 @@ namespace ts { ES3 = 0, ES5 = 1, ES6 = 2, + ES7 = 3, ES2015 = ES6, - Latest = ES6, + ES2016 = ES7, + Latest = ES7, } export const enum LanguageVariant { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2ab7f15549b..15e288ef601 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -11,7 +11,8 @@ namespace ts { export interface SynthesizedNode extends Node { leadingCommentRanges?: CommentRange[]; trailingCommentRanges?: CommentRange[]; - startsOnNewLine: boolean; + startsOnNewLine?: boolean; + disableSourceMap?: boolean; } export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration { @@ -2724,6 +2725,68 @@ namespace ts { return carriageReturnLineFeed; } + /** + * Tests whether a node and its subtree is simple enough to have its position + * information ignored when emitting source maps in a destructuring assignment. + * + * @param node The expression to test. + */ + export function isSimpleExpression(node: Expression): boolean { + return isSimpleExpressionWorker(node, 0); + } + + function isSimpleExpressionWorker(node: Expression, depth: number): boolean { + if (depth <= 5) { + const kind = node.kind; + if (kind === SyntaxKind.StringLiteral + || kind === SyntaxKind.NumericLiteral + || kind === SyntaxKind.RegularExpressionLiteral + || kind === SyntaxKind.NoSubstitutionTemplateLiteral + || kind === SyntaxKind.Identifier + || kind === SyntaxKind.ThisKeyword + || kind === SyntaxKind.SuperKeyword + || kind === SyntaxKind.TrueKeyword + || kind === SyntaxKind.FalseKeyword + || kind === SyntaxKind.NullKeyword) { + return true; + } + else if (kind === SyntaxKind.PropertyAccessExpression) { + return isSimpleExpressionWorker((node).expression, depth + 1); + } + else if (kind === SyntaxKind.ElementAccessExpression) { + return isSimpleExpressionWorker((node).expression, depth + 1) + && isSimpleExpressionWorker((node).argumentExpression, depth + 1); + } + else if (kind === SyntaxKind.PrefixUnaryExpression + || kind === SyntaxKind.PostfixUnaryExpression) { + return isSimpleExpressionWorker((node).operand, depth + 1); + } + else if (kind === SyntaxKind.BinaryExpression) { + return (node).operatorToken.kind !== SyntaxKind.AsteriskAsteriskToken + && isSimpleExpressionWorker((node).left, depth + 1) + && isSimpleExpressionWorker((node).right, depth + 1); + } + else if (kind === SyntaxKind.ConditionalExpression) { + return isSimpleExpressionWorker((node).condition, depth + 1) + && isSimpleExpressionWorker((node).whenTrue, depth + 1) + && isSimpleExpressionWorker((node).whenFalse, depth + 1) + } + else if (kind === SyntaxKind.VoidExpression + || kind === SyntaxKind.TypeOfExpression + || kind === SyntaxKind.DeleteExpression) { + return isSimpleExpressionWorker((node).expression, depth + 1); + } + else if (kind === SyntaxKind.ArrayLiteralExpression) { + return (node).elements.length === 0; + } + else if (kind === SyntaxKind.ObjectLiteralExpression) { + return (node).properties.length === 0; + } + } + + return false; + } + // Node tests // // All node tests in the following list should *not* reference parent pointers so that