From 83f5d3f25ccfd184164ec1572f2b4394789b3c3b Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 21 Oct 2016 18:44:13 -0700 Subject: [PATCH 01/26] Move emit helpers into related transformers --- src/compiler/checker.ts | 2 +- src/compiler/core.ts | 23 +- src/compiler/emitter.ts | 290 ++-------- src/compiler/factory.ts | 617 +++++++++++---------- src/compiler/transformers/es2015.ts | 39 +- src/compiler/transformers/es2017.ts | 137 +++-- src/compiler/transformers/generators.ts | 159 +++++- src/compiler/transformers/jsx.ts | 553 +++++++++--------- src/compiler/transformers/module/es2015.ts | 22 +- src/compiler/transformers/module/module.ts | 16 +- src/compiler/transformers/module/system.ts | 22 +- src/compiler/transformers/ts.ts | 158 ++++-- src/compiler/types.ts | 26 +- src/compiler/utilities.ts | 147 ----- 14 files changed, 1104 insertions(+), 1107 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4fa14658494..e6617c88eb3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19034,7 +19034,7 @@ namespace ts { function isNameOfModuleOrEnumDeclaration(node: Identifier) { const parent = node.parent; - return isModuleOrEnumDeclaration(parent) && node === parent.name; + return parent && isModuleOrEnumDeclaration(parent) && node === parent.name; } // When resolved as an expression identifier, if the given node references an exported entity, return the declaration diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 13813182d32..ac881200837 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -538,7 +538,7 @@ namespace ts { */ export function append(to: T[] | undefined, value: T | undefined): T[] | undefined { if (value === undefined) return to; - if (to === undefined) to = []; + if (to === undefined) return [value]; to.push(value); return to; } @@ -559,6 +559,16 @@ namespace ts { return to; } + /** + * Stable sort of an array. Elements equal to each other maintain their relative position in the array. + */ + export function stableSort(array: T[], comparer: (x: T, y: T) => Comparison = compareValues) { + return array + .map((_, i) => i) // create array of indices + .sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)) // sort indices by value then position + .map(i => array[i]); // get sorted array + } + export function rangeEquals(array1: T[], array2: T[], pos: number, end: number) { while (pos < end) { if (array1[pos] !== array2[pos]) { @@ -2022,6 +2032,17 @@ namespace ts { } /** Remove an item from an array, moving everything to its right one space left. */ + export function orderedRemoveItem(array: T[], item: T): boolean { + for (let i = 0; i < array.length; i++) { + if (array[i] === item) { + orderedRemoveItemAt(array, i); + return true; + } + } + return false; + } + + /** Remove an item by index from an array, moving everything to its right one space left. */ export function orderedRemoveItemAt(array: T[], index: number): void { // This seems to be faster than either `array.splice(i, 1)` or `array.copyWithin(i, i+ 1)`. for (let i = index; i < array.length - 1; i++) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 1051c9adca9..ed16ec06159 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -21,153 +21,6 @@ namespace ts { const delimiters = createDelimiterMap(); const brackets = createBracketsMap(); - // emit output for the __extends helper function - const extendsHelper = ` -var __extends = (this && this.__extends) || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -};`; - - // Emit output for the __assign helper function. - // This is typically used for JSX spread attributes, - // and can be used for object literal spread properties. - const assignHelper = ` -var __assign = (this && this.__assign) || Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; -};`; - - // emit output for the __decorate helper function - const decorateHelper = ` -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -};`; - - // emit output for the __metadata helper function - const metadataHelper = ` -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -};`; - - // emit output for the __param helper function - const paramHelper = ` -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -};`; - - // emit output for the __awaiter helper function - const awaiterHelper = ` -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments)).next()); - }); -};`; - - // The __generator helper is used by down-level transformations to emulate the runtime - // semantics of an ES2015 generator function. When called, this helper returns an - // object that implements the Iterator protocol, in that it has `next`, `return`, and - // `throw` methods that step through the generator when invoked. - // - // parameters: - // thisArg The value to use as the `this` binding for the transformed generator body. - // body A function that acts as the transformed generator body. - // - // variables: - // _ Persistent state for the generator that is shared between the helper and the - // generator body. The state object has the following members: - // sent() - A method that returns or throws the current completion value. - // label - The next point at which to resume evaluation of the generator body. - // trys - A stack of protected regions (try/catch/finally blocks). - // ops - A stack of pending instructions when inside of a finally block. - // f A value indicating whether the generator is executing. - // y An iterator to delegate for a yield*. - // t A temporary variable that holds one of the following values (note that these - // cases do not overlap): - // - The completion value when resuming from a `yield` or `yield*`. - // - The error value for a catch block. - // - The current protected region (array of try/catch/finally/end labels). - // - The verb (`next`, `throw`, or `return` method) to delegate to the expression - // of a `yield*`. - // - The result of evaluating the verb delegated to the expression of a `yield*`. - // - // functions: - // verb(n) Creates a bound callback to the `step` function for opcode `n`. - // step(op) Evaluates opcodes in a generator body until execution is suspended or - // completed. - // - // The __generator helper understands a limited set of instructions: - // 0: next(value?) - Start or resume the generator with the specified value. - // 1: throw(error) - Resume the generator with an exception. If the generator is - // suspended inside of one or more protected regions, evaluates - // any intervening finally blocks between the current label and - // the nearest catch block or function boundary. If uncaught, the - // exception is thrown to the caller. - // 2: return(value?) - Resume the generator as if with a return. If the generator is - // suspended inside of one or more protected regions, evaluates any - // intervening finally blocks. - // 3: break(label) - Jump to the specified label. If the label is outside of the - // current protected region, evaluates any intervening finally - // blocks. - // 4: yield(value?) - Yield execution to the caller with an optional value. When - // resumed, the generator will continue at the next label. - // 5: yield*(value) - Delegates evaluation to the supplied iterator. When - // delegation completes, the generator will continue at the next - // label. - // 6: catch(error) - Handles an exception thrown from within the generator body. If - // the current label is inside of one or more protected regions, - // evaluates any intervening finally blocks between the current - // label and the nearest catch block or function boundary. If - // uncaught, the exception is thrown to the caller. - // 7: endfinally - Ends a finally block, resuming the last instruction prior to - // entering a finally block. - // - // For examples of how these are used, see the comments in ./transformers/generators.ts - const generatorHelper = ` -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t; - return { next: verb(0), "throw": verb(1), "return": verb(2) }; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [0, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -};`; - - // emit output for the __export helper function - const exportStarHelper = ` -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -}`; - // emit output for the UMD helper function. const umdHelper = ` (function (dependencies, factory) { @@ -179,15 +32,6 @@ function __export(m) { } })`; - const superHelper = ` -const _super = name => super[name];`; - - const advancedSuperHelper = ` -const _super = (function (geti, seti) { - const cache = Object.create(null); - return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); -})(name => super[name], (name, value) => super[name] = value);`; - const compilerOptions = host.getCompilerOptions(); const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); @@ -224,11 +68,7 @@ const _super = (function (geti, seti) { let currentSourceFile: SourceFile; let currentText: string; let currentFileIdentifiers: Map; - let extendsEmitted: boolean; - let assignEmitted: boolean; - let decorateEmitted: boolean; - let paramEmitted: boolean; - let awaiterEmitted: boolean; + let bundledHelpers: Map; let isOwnFileEmit: boolean; let emitSkipped = false; @@ -293,12 +133,13 @@ const _super = (function (geti, seti) { nodeIdToGeneratedName = []; autoGeneratedIdToGeneratedName = []; generatedNameSet = createMap(); + bundledHelpers = isBundledEmit ? createMap() : undefined; isOwnFileEmit = !isBundledEmit; // Emit helpers from all the files if (isBundledEmit && moduleKind) { for (const sourceFile of sourceFiles) { - emitEmitHelpers(sourceFile); + emitHelpers(sourceFile, /*isBundle*/ true); } } @@ -333,11 +174,6 @@ const _super = (function (geti, seti) { tempFlags = TempFlags.Auto; currentSourceFile = undefined; currentText = undefined; - extendsEmitted = false; - assignEmitted = false; - decorateEmitted = false; - paramEmitted = false; - awaiterEmitted = false; isOwnFileEmit = false; } @@ -2141,85 +1977,39 @@ const _super = (function (geti, seti) { return statements.length; } - function emitHelpers(node: Node) { - const emitFlags = getEmitFlags(node); - let helpersEmitted = false; - if (emitFlags & EmitFlags.EmitEmitHelpers) { - helpersEmitted = emitEmitHelpers(currentSourceFile); - } - - if (emitFlags & EmitFlags.EmitExportStar) { - writeLines(exportStarHelper); - helpersEmitted = true; - } - - if (emitFlags & EmitFlags.EmitSuperHelper) { - writeLines(superHelper); - helpersEmitted = true; - } - - if (emitFlags & EmitFlags.EmitAdvancedSuperHelper) { - writeLines(advancedSuperHelper); - helpersEmitted = true; - } - - return helpersEmitted; - } - - function emitEmitHelpers(node: SourceFile) { - // Only emit helpers if the user did not say otherwise. - if (compilerOptions.noEmitHelpers) { - return false; - } - - // Don't emit helpers if we can import them. - if (compilerOptions.importHelpers - && (isExternalModule(node) || compilerOptions.isolatedModules)) { - return false; - } + function emitHelpers(node: Node, isBundle?: boolean) { + const sourceFile = isSourceFile(node) ? node : currentSourceFile; + const shouldSkip = compilerOptions.noEmitHelpers || (sourceFile && getExternalHelpersModuleName(sourceFile) !== undefined); + const shouldBundle = isSourceFile(node) && !isOwnFileEmit; let helpersEmitted = false; + const helpers = getEmitHelpers(node); + if (helpers) { + for (const helper of stableSort(helpers, compareEmitHelpers)) { + if (!helper.scoped) { + // Skip the helper if it can be skipped and the noEmitHelpers compiler + // option is set, or if it can be imported and the importHelpers compiler + // option is set. + if (shouldSkip) continue; - // Only Emit __extends function when target ES5. - // For target ES6 and above, we can emit classDeclaration as is. - if ((languageVersion < ScriptTarget.ES2015) && (!extendsEmitted && node.flags & NodeFlags.HasClassExtends)) { - writeLines(extendsHelper); - extendsEmitted = true; - helpersEmitted = true; - } + // Skip the helper if it can be bundled but hasn't already been emitted and we + // are emitting a bundled module. + if (shouldBundle) { + if (helper.name in bundledHelpers) { + continue; + } - if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasJsxSpreadAttributes)) { - writeLines(assignHelper); - assignEmitted = true; - } + bundledHelpers[helper.name] = true; + } + } + else if (isBundle) { + // Skip the helper if it is scoped and we are emitting bundled helpers + continue; + } - if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) { - writeLines(decorateHelper); - if (compilerOptions.emitDecoratorMetadata) { - writeLines(metadataHelper); + writeLines(helper.text); + helpersEmitted = true; } - - decorateEmitted = true; - helpersEmitted = true; - } - - if (!paramEmitted && node.flags & NodeFlags.HasParamDecorators) { - writeLines(paramHelper); - paramEmitted = true; - helpersEmitted = true; - } - - // Only emit __awaiter function when target ES5/ES6. - // Only emit __generator function when target ES5. - // For target ES2017 and above, we can emit async/await as is. - if ((languageVersion < ScriptTarget.ES2017) && (!awaiterEmitted && node.flags & NodeFlags.HasAsyncFunctions)) { - writeLines(awaiterHelper); - if (languageVersion < ScriptTarget.ES2015) { - writeLines(generatorHelper); - } - - awaiterEmitted = true; - helpersEmitted = true; } if (helpersEmitted) { @@ -2230,9 +2020,10 @@ const _super = (function (geti, seti) { } function writeLines(text: string): void { - const lines = text.split(/\r\n|\r|\n/g); + const lines = text.split(/\r\n?|\n/g); + const indentation = guessIndentation(lines); for (let i = 0; i < lines.length; i++) { - const line = lines[i]; + const line = indentation ? lines[i].slice(indentation) : lines[i]; if (line.length) { if (i > 0) { writeLine(); @@ -2242,6 +2033,21 @@ const _super = (function (geti, seti) { } } + function guessIndentation(lines: string[]) { + let indentation: number; + for (const line of lines) { + for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) { + if (!isWhiteSpace(line.charCodeAt(i))) { + if (indentation === undefined || i < indentation) { + indentation = i; + break; + } + } + } + } + return indentation; + } + // // Helpers // diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index c557e3514a5..04b7079d094 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1440,7 +1440,6 @@ namespace ts { if (node.resolvedTypeReferenceDirectiveNames !== undefined) updated.resolvedTypeReferenceDirectiveNames = node.resolvedTypeReferenceDirectiveNames; if (node.imports !== undefined) updated.imports = node.imports; if (node.moduleAugmentations !== undefined) updated.moduleAugmentations = node.moduleAugmentations; - if (node.externalHelpersModuleName !== undefined) updated.externalHelpersModuleName = node.externalHelpersModuleName; return updateNode(updated, node); } @@ -1683,278 +1682,23 @@ namespace ts { // Helpers - export function createHelperName(externalHelpersModuleName: Identifier | undefined, name: string) { + export interface EmitHelperState { + currentSourceFile: SourceFile; + compilerOptions: CompilerOptions; + requestedHelpers?: EmitHelper[]; + } + + export function getHelperName(helperState: EmitHelperState, name: string) { + const externalHelpersModuleName = getOrCreateExternalHelpersModuleName(helperState.currentSourceFile, helperState.compilerOptions); return externalHelpersModuleName ? createPropertyAccess(externalHelpersModuleName, name) : createIdentifier(name); } - export function createExtendsHelper(externalHelpersModuleName: Identifier | undefined, name: Identifier) { - return createCall( - createHelperName(externalHelpersModuleName, "__extends"), - /*typeArguments*/ undefined, - [ - name, - createIdentifier("_super") - ] - ); - } - - export function createAssignHelper(externalHelpersModuleName: Identifier | undefined, attributesSegments: Expression[]) { - return createCall( - createHelperName(externalHelpersModuleName, "__assign"), - /*typeArguments*/ undefined, - attributesSegments - ); - } - - export function createParamHelper(externalHelpersModuleName: Identifier | undefined, expression: Expression, parameterOffset: number, location?: TextRange) { - return createCall( - createHelperName(externalHelpersModuleName, "__param"), - /*typeArguments*/ undefined, - [ - createLiteral(parameterOffset), - expression - ], - location - ); - } - - export function createMetadataHelper(externalHelpersModuleName: Identifier | undefined, metadataKey: string, metadataValue: Expression) { - return createCall( - createHelperName(externalHelpersModuleName, "__metadata"), - /*typeArguments*/ undefined, - [ - createLiteral(metadataKey), - metadataValue - ] - ); - } - - export function createDecorateHelper(externalHelpersModuleName: Identifier | undefined, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) { - const argumentsArray: Expression[] = []; - argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true)); - argumentsArray.push(target); - if (memberName) { - argumentsArray.push(memberName); - if (descriptor) { - argumentsArray.push(descriptor); - } + export function requestEmitHelper(helperState: EmitHelperState, helper: EmitHelper) { + if (!contains(helperState.requestedHelpers, helper)) { + helperState.requestedHelpers = append(helperState.requestedHelpers, helper); } - - return createCall(createHelperName(externalHelpersModuleName, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location); - } - - export function createAwaiterHelper(externalHelpersModuleName: Identifier | undefined, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { - const generatorFunc = createFunctionExpression( - /*modifiers*/ undefined, - createToken(SyntaxKind.AsteriskToken), - /*name*/ undefined, - /*typeParameters*/ undefined, - /*parameters*/ [], - /*type*/ undefined, - body - ); - - // Mark this node as originally an async function - (generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody; - - return createCall( - createHelperName(externalHelpersModuleName, "__awaiter"), - /*typeArguments*/ undefined, - [ - createThis(), - hasLexicalArguments ? createIdentifier("arguments") : createVoidZero(), - promiseConstructor ? createExpressionFromEntityName(promiseConstructor) : createVoidZero(), - generatorFunc - ] - ); - } - - export function createHasOwnProperty(target: LeftHandSideExpression, propertyName: Expression) { - return createCall( - createPropertyAccess(target, "hasOwnProperty"), - /*typeArguments*/ undefined, - [propertyName] - ); - } - - function createObjectCreate(prototype: Expression) { - return createCall( - createPropertyAccess(createIdentifier("Object"), "create"), - /*typeArguments*/ undefined, - [prototype] - ); - } - - function createGeti(target: LeftHandSideExpression) { - // name => super[name] - return createArrowFunction( - /*modifiers*/ undefined, - /*typeParameters*/ undefined, - [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "name")], - /*type*/ undefined, - createToken(SyntaxKind.EqualsGreaterThanToken), - createElementAccess(target, createIdentifier("name")) - ); - } - - function createSeti(target: LeftHandSideExpression) { - // (name, value) => super[name] = value - return createArrowFunction( - /*modifiers*/ undefined, - /*typeParameters*/ undefined, - [ - createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "name"), - createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "value") - ], - /*type*/ undefined, - createToken(SyntaxKind.EqualsGreaterThanToken), - createAssignment( - createElementAccess( - target, - createIdentifier("name") - ), - createIdentifier("value") - ) - ); - } - - export function createAdvancedAsyncSuperHelper() { - // const _super = (function (geti, seti) { - // const cache = Object.create(null); - // return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); - // })(name => super[name], (name, value) => super[name] = value); - - // const cache = Object.create(null); - const createCache = createVariableStatement( - /*modifiers*/ undefined, - createConstDeclarationList([ - createVariableDeclaration( - "cache", - /*type*/ undefined, - createObjectCreate(createNull()) - ) - ]) - ); - - // get value() { return geti(name); } - const getter = createGetAccessor( - /*decorators*/ undefined, - /*modifiers*/ undefined, - "value", - /*parameters*/ [], - /*type*/ undefined, - createBlock([ - createReturn( - createCall( - createIdentifier("geti"), - /*typeArguments*/ undefined, - [createIdentifier("name")] - ) - ) - ]) - ); - - // set value(v) { seti(name, v); } - const setter = createSetAccessor( - /*decorators*/ undefined, - /*modifiers*/ undefined, - "value", - [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "v")], - createBlock([ - createStatement( - createCall( - createIdentifier("seti"), - /*typeArguments*/ undefined, - [ - createIdentifier("name"), - createIdentifier("v") - ] - ) - ) - ]) - ); - - // return name => cache[name] || ... - const getOrCreateAccessorsForName = createReturn( - createArrowFunction( - /*modifiers*/ undefined, - /*typeParameters*/ undefined, - [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "name")], - /*type*/ undefined, - createToken(SyntaxKind.EqualsGreaterThanToken), - createLogicalOr( - createElementAccess( - createIdentifier("cache"), - createIdentifier("name") - ), - createParen( - createAssignment( - createElementAccess( - createIdentifier("cache"), - createIdentifier("name") - ), - createObjectLiteral([ - getter, - setter - ]) - ) - ) - ) - ) - ); - - // const _super = (function (geti, seti) { - // const cache = Object.create(null); - // return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); - // })(name => super[name], (name, value) => super[name] = value); - return createVariableStatement( - /*modifiers*/ undefined, - createConstDeclarationList([ - createVariableDeclaration( - "_super", - /*type*/ undefined, - createCall( - createParen( - createFunctionExpression( - /*modifiers*/ undefined, - /*asteriskToken*/ undefined, - /*name*/ undefined, - /*typeParameters*/ undefined, - [ - createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "geti"), - createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "seti") - ], - /*type*/ undefined, - createBlock([ - createCache, - getOrCreateAccessorsForName - ]) - ) - ), - /*typeArguments*/ undefined, - [ - createGeti(createSuper()), - createSeti(createSuper()) - ] - ) - ) - ]) - ); - } - - export function createSimpleAsyncSuperHelper() { - return createVariableStatement( - /*modifiers*/ undefined, - createConstDeclarationList([ - createVariableDeclaration( - "_super", - /*type*/ undefined, - createGeti(createSuper()) - ) - ]) - ); } export interface CallBinding { @@ -2356,11 +2100,11 @@ namespace ts { /** * Ensures "use strict" directive is added * - * @param node source file + * @param statements An array of statements */ - export function ensureUseStrict(node: SourceFile): SourceFile { + export function ensureUseStrict(statements: NodeArray): NodeArray { let foundUseStrict = false; - for (const statement of node.statements) { + for (const statement of statements) { if (isPrologueDirective(statement)) { if (isUseStrictPrologue(statement as ExpressionStatement)) { foundUseStrict = true; @@ -2371,13 +2115,15 @@ namespace ts { break; } } + if (!foundUseStrict) { - const statements: Statement[] = []; - statements.push(startOnNewLine(createStatement(createLiteral("use strict")))); - // add "use strict" as the first statement - return updateSourceFileNode(node, statements.concat(node.statements)); + return createNodeArray([ + startOnNewLine(createStatement(createLiteral("use strict"))), + ...statements + ], statements); } - return node; + + return statements; } /** @@ -2796,12 +2542,21 @@ namespace ts { } function mergeEmitNode(sourceEmitNode: EmitNode, destEmitNode: EmitNode) { - const { flags, commentRange, sourceMapRange, tokenSourceMapRanges } = sourceEmitNode; - if (!destEmitNode && (flags || commentRange || sourceMapRange || tokenSourceMapRanges)) destEmitNode = {}; + const { + flags, + commentRange, + sourceMapRange, + tokenSourceMapRanges, + constantValue, + helpers + } = sourceEmitNode; + if (!destEmitNode) destEmitNode = {}; if (flags) destEmitNode.flags = flags; if (commentRange) destEmitNode.commentRange = commentRange; if (sourceMapRange) destEmitNode.sourceMapRange = sourceMapRange; if (tokenSourceMapRanges) destEmitNode.tokenSourceMapRanges = mergeTokenSourceMapRanges(tokenSourceMapRanges, destEmitNode.tokenSourceMapRanges); + if (constantValue !== undefined) destEmitNode.constantValue = constantValue; + if (helpers) destEmitNode.helpers = addRange(destEmitNode.helpers, helpers); return destEmitNode; } @@ -2879,6 +2634,16 @@ namespace ts { return node; } + /** + * Gets a custom text range to use when emitting source maps. + * + * @param node The node. + */ + export function getSourceMapRange(node: Node) { + const emitNode = node.emitNode; + return (emitNode && emitNode.sourceMapRange) || node; + } + /** * Sets a custom text range to use when emitting source maps. * @@ -2890,6 +2655,18 @@ namespace ts { return node; } + /** + * Gets the TextRange to use for source maps for a token of a node. + * + * @param node The node. + * @param token The token. + */ + export function getTokenSourceMapRange(node: Node, token: SyntaxKind) { + const emitNode = node.emitNode; + const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges; + return tokenSourceMapRanges && tokenSourceMapRanges[token]; + } + /** * Sets the TextRange to use for source maps for a token of a node. * @@ -2904,14 +2681,6 @@ namespace ts { return node; } - /** - * Sets a custom text range to use when emitting comments. - */ - export function setCommentRange(node: T, range: TextRange) { - getOrCreateEmitNode(node).commentRange = range; - return node; - } - /** * Gets a custom text range to use when emitting comments. * @@ -2923,25 +2692,11 @@ namespace ts { } /** - * Gets a custom text range to use when emitting source maps. - * - * @param node The node. + * Sets a custom text range to use when emitting comments. */ - export function getSourceMapRange(node: Node) { - const emitNode = node.emitNode; - return (emitNode && emitNode.sourceMapRange) || node; - } - - /** - * Gets the TextRange to use for source maps for a token of a node. - * - * @param node The node. - * @param token The token. - */ - export function getTokenSourceMapRange(node: Node, token: SyntaxKind) { - const emitNode = node.emitNode; - const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges; - return tokenSourceMapRanges && tokenSourceMapRanges[token]; + export function setCommentRange(node: T, range: TextRange) { + getOrCreateEmitNode(node).commentRange = range; + return node; } /** @@ -2961,6 +2716,102 @@ namespace ts { return node; } + export function getExternalHelpersModuleName(node: SourceFile) { + const parseNode = getOriginalNode(node, isSourceFile); + const emitNode = parseNode && parseNode.emitNode; + return emitNode && emitNode.externalHelpersModuleName; + } + + export function getOrCreateExternalHelpersModuleName(node: SourceFile, compilerOptions: CompilerOptions) { + if (compilerOptions.importHelpers && (isExternalModule(node) || compilerOptions.isolatedModules)) { + const parseNode = getOriginalNode(node, isSourceFile); + const emitNode = getOrCreateEmitNode(parseNode); + return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText)); + } + } + + /** + * Adds an EmitHelper to a node. + */ + export function addEmitHelper(node: T, helper: EmitHelper): T { + const emitNode = getOrCreateEmitNode(node); + emitNode.helpers = append(emitNode.helpers, helper); + return node; + } + + /** + * Adds an EmitHelper to a node. + */ + export function addEmitHelpers(node: T, helpers: EmitHelper[] | undefined): T { + if (some(helpers)) { + const emitNode = getOrCreateEmitNode(node); + for (const helper of helpers) { + if (!contains(emitNode.helpers, helper)) { + emitNode.helpers = append(emitNode.helpers, helper); + } + } + } + return node; + } + + /** + * Removes an EmitHelper from a node. + */ + export function removeEmitHelper(node: Node, helper: EmitHelper): boolean { + const emitNode = node.emitNode; + if (emitNode) { + const helpers = emitNode.helpers; + if (helpers) { + return orderedRemoveItem(helpers, helper); + } + } + return false; + } + + /** + * Gets the EmitHelpers of a node. + */ + export function getEmitHelpers(node: Node): EmitHelper[] | undefined { + const emitNode = node.emitNode; + return emitNode && emitNode.helpers; + } + + /** + * Moves matching emit helpers from a source node to a target node. + */ + export function moveEmitHelpers(source: Node, target: Node, predicate: (helper: EmitHelper) => boolean) { + const sourceEmitNode = source.emitNode; + const sourceEmitHelpers = sourceEmitNode && sourceEmitNode.helpers; + if (!some(sourceEmitHelpers)) return; + + const targetEmitNode = getOrCreateEmitNode(target); + let helpersRemoved = 0; + for (let i = 0; i < sourceEmitHelpers.length; i++) { + const helper = sourceEmitHelpers[i]; + if (predicate(helper)) { + helpersRemoved++; + if (!contains(targetEmitNode.helpers, helper)) { + targetEmitNode.helpers = append(targetEmitNode.helpers, helper); + } + } + else if (helpersRemoved > 0) { + sourceEmitHelpers[i - helpersRemoved] = helper; + } + } + + if (helpersRemoved > 0) { + sourceEmitHelpers.length -= helpersRemoved; + } + } + + export function compareEmitHelpers(x: EmitHelper, y: EmitHelper) { + if (x === y) return Comparison.EqualTo; + if (x.priority === y.priority) return Comparison.EqualTo; + if (x.priority === undefined) return Comparison.GreaterThan; + if (y.priority === undefined) return Comparison.LessThan; + return compareValues(x.priority, y.priority); + } + export function setTextRange(node: T, location: TextRange): T { if (location) { node.pos = location.pos; @@ -3055,4 +2906,164 @@ namespace ts { function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) { return tryGetModuleNameFromFile(resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions); } + + export interface ExternalModuleInfo { + externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules + externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers + exportSpecifiers: Map; // export specifiers by name + exportedBindings: Map; // exported names of local declarations + exportedNames: Identifier[]; // all exported names local to module + exportEquals: ExportAssignment | undefined; // an export= declaration if one was present + hasExportStarsToExportValues: boolean; // whether this module contains export* + } + + export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver): ExternalModuleInfo { + const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; + const exportSpecifiers = createMap(); + const exportedBindings = createMap(); + const uniqueExports = createMap(); + let hasExportDefault = false; + let exportEquals: ExportAssignment = undefined; + let hasExportStarsToExportValues = false; + + const externalHelpersModuleName = getExternalHelpersModuleName(sourceFile); + const externalHelpersImportDeclaration = externalHelpersModuleName && createImportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)), + createLiteral(externalHelpersModuleNameText)); + + if (externalHelpersImportDeclaration) { + externalImports.push(externalHelpersImportDeclaration); + } + + for (const node of sourceFile.statements) { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + // import "mod" + // import x from "mod" + // import * as x from "mod" + // import { x, y } from "mod" + externalImports.push(node); + break; + + case SyntaxKind.ImportEqualsDeclaration: + if ((node).moduleReference.kind === SyntaxKind.ExternalModuleReference) { + // import x = require("mod") + externalImports.push(node); + } + + break; + + case SyntaxKind.ExportDeclaration: + if ((node).moduleSpecifier) { + if (!(node).exportClause) { + // export * from "mod" + externalImports.push(node); + hasExportStarsToExportValues = true; + } + else { + // export { x, y } from "mod" + externalImports.push(node); + } + } + else { + // export { x, y } + for (const specifier of (node).exportClause.elements) { + if (!uniqueExports[specifier.name.text]) { + const name = specifier.propertyName || specifier.name; + multiMapAdd(exportSpecifiers, name.text, specifier); + + const decl = resolver.getReferencedImportDeclaration(name) + || resolver.getReferencedValueDeclaration(name); + + if (decl) { + multiMapAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); + } + + uniqueExports[specifier.name.text] = specifier.name; + } + } + } + break; + + case SyntaxKind.ExportAssignment: + if ((node).isExportEquals && !exportEquals) { + // export = x + exportEquals = node; + } + break; + + case SyntaxKind.VariableStatement: + if (hasModifier(node, ModifierFlags.Export)) { + for (const decl of (node).declarationList.declarations) { + collectExportedVariableInfo(decl, uniqueExports); + } + } + break; + + case SyntaxKind.FunctionDeclaration: + if (hasModifier(node, ModifierFlags.Export)) { + if (hasModifier(node, ModifierFlags.Default)) { + // export default function() { } + if (!hasExportDefault) { + multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); + hasExportDefault = true; + } + } + else { + // export function x() { } + const name = (node).name; + if (!uniqueExports[name.text]) { + multiMapAdd(exportedBindings, getOriginalNodeId(node), name); + uniqueExports[name.text] = name; + } + } + } + break; + + case SyntaxKind.ClassDeclaration: + if (hasModifier(node, ModifierFlags.Export)) { + if (hasModifier(node, ModifierFlags.Default)) { + // export default class { } + if (!hasExportDefault) { + multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); + hasExportDefault = true; + } + } + else { + // export class x { } + const name = (node).name; + if (!uniqueExports[name.text]) { + multiMapAdd(exportedBindings, getOriginalNodeId(node), name); + uniqueExports[name.text] = name; + } + } + } + break; + } + } + + let exportedNames: Identifier[]; + for (const key in uniqueExports) { + exportedNames = ts.append(exportedNames, uniqueExports[key]); + } + + return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames, externalHelpersImportDeclaration }; + } + + function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map) { + if (isBindingPattern(decl.name)) { + for (const element of decl.name.elements) { + if (!isOmittedExpression(element)) { + collectExportedVariableInfo(element, uniqueExports); + } + } + } + else if (!isGeneratedIdentifier(decl.name)) { + if (!uniqueExports[decl.name.text]) { + uniqueExports[decl.name.text] = decl.name; + } + } + } } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 25c15305c4a..60d1ad95682 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -3,7 +3,6 @@ /*@internal*/ namespace ts { - const enum ES2015SubstitutionFlags { /** Enables substitutions for captured `this` */ CapturedThis = 1 << 0, @@ -170,6 +169,7 @@ namespace ts { hoistVariableDeclaration, } = context; + const compilerOptions = context.getCompilerOptions(); const resolver = context.getEmitResolver(); const previousOnSubstituteNode = context.onSubstituteNode; const previousOnEmitNode = context.onEmitNode; @@ -187,6 +187,7 @@ namespace ts { let enclosingNonArrowFunction: FunctionLikeDeclaration; let enclosingNonAsyncFunctionBody: FunctionLikeDeclaration | ClassElement; let isInConstructorWithCapturedSuper: boolean; + let helperState: EmitHelperState; /** * Used to track if we are emitting body of the converted loop @@ -209,7 +210,15 @@ namespace ts { currentSourceFile = node; currentText = node.text; - return visitNode(node, visitor, isSourceFile); + helperState = { currentSourceFile, compilerOptions }; + + const visited = visitNode(node, visitor, isSourceFile); + addEmitHelpers(visited, helperState.requestedHelpers); + + currentSourceFile = undefined; + currentText = undefined; + helperState = undefined; + return visited; } function visitor(node: Node): VisitResult { @@ -778,7 +787,7 @@ namespace ts { if (extendsClauseElement) { statements.push( createStatement( - createExtendsHelper(currentSourceFile.externalHelpersModuleName, getLocalName(node)), + createExtendsHelper(helperState, getLocalName(node)), /*location*/ extendsClauseElement ) ); @@ -3274,4 +3283,28 @@ namespace ts { return isIdentifier(expression) && expression === parameter.name; } } + + function createExtendsHelper(helperState: EmitHelperState, name: Identifier) { + requestEmitHelper(helperState, extendsHelper); + return createCall( + getHelperName(helperState, "__extends"), + /*typeArguments*/ undefined, + [ + name, + createIdentifier("_super") + ] + ); + } + + const extendsHelper: EmitHelper = { + name: "typescript:extends", + scoped: false, + priority: 0, + text: ` + var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + };` + }; } diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 7800a41e147..14d97ade2ac 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -5,13 +5,12 @@ namespace ts { type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration; + const enum ES2017SubstitutionFlags { + /** Enables substitutions for async methods with `super` calls. */ + AsyncMethodsWithSuper = 1 << 0 + } + export function transformES2017(context: TransformationContext) { - - const enum ES2017SubstitutionFlags { - /** Enables substitutions for async methods with `super` calls. */ - AsyncMethodsWithSuper = 1 << 0 - } - const { startLexicalEnvironment, endLexicalEnvironment, @@ -22,7 +21,9 @@ namespace ts { const languageVersion = getEmitScriptTarget(compilerOptions); // These variables contain state that changes as we descend into the tree. - let currentSourceFileExternalHelpersModuleName: Identifier; + let currentSourceFile: SourceFile; + let helperState: EmitHelperState; + /** * Keeps track of whether expression substitution has been enabled for specific edge cases. * They are persisted between each SourceFile transformation and should not be reset. @@ -58,9 +59,16 @@ namespace ts { return node; } - currentSourceFileExternalHelpersModuleName = node.externalHelpersModuleName; + currentSourceFile = node; + helperState = { currentSourceFile, compilerOptions }; - return visitEachChild(node, visitor, context); + const visited = visitEachChild(node, visitor, context); + + addEmitHelpers(visited, helperState.requestedHelpers); + + currentSourceFile = undefined; + helperState = undefined; + return visited; } function visitor(node: Node): VisitResult { @@ -107,11 +115,11 @@ namespace ts { } /** - * Visits an await expression. + * Visits an AwaitExpression node. * * This function will be called any time a ES2017 await expression is encountered. * - * @param node The await expression node. + * @param node The node to visit. */ function visitAwaitExpression(node: AwaitExpression): Expression { return setOriginalNode( @@ -125,17 +133,15 @@ namespace ts { } /** - * Visits a method declaration of a class. + * Visits a MethodDeclaration node. * * This function will be called when one of the following conditions are met: * - The node is marked as async * - * @param node The method node. + * @param node The node to visit. */ function visitMethodDeclaration(node: MethodDeclaration) { - if (!isAsyncFunctionLike(node)) { - return node; - } + Debug.assert(hasModifier(node, ModifierFlags.Async)); const method = createMethod( /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), @@ -150,25 +156,20 @@ namespace ts { // While we emit the source map for the node after skipping decorators and modifiers, // we need to emit the comments for the original range. - setCommentRange(method, node); - setSourceMapRange(method, moveRangePastDecorators(node)); setOriginalNode(method, node); - return method; } /** - * Visits a function declaration. + * Visits a FunctionDeclaration node. * * This function will be called when one of the following conditions are met: * - The node is marked async * - * @param node The function node. + * @param node The node to visit. */ function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { - if (!isAsyncFunctionLike(node)) { - return node; - } + Debug.assert(hasModifier(node, ModifierFlags.Async)); const func = createFunctionDeclaration( /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), @@ -180,23 +181,21 @@ namespace ts { transformFunctionBody(node), /*location*/ node ); - setOriginalNode(func, node); + setOriginalNode(func, node); return func; } /** - * Visits a function expression node. + * Visits a FunctionExpression node. * * This function will be called when one of the following conditions are met: * - The node is marked async * - * @param node The function expression node. + * @param node The node to visit. */ function visitFunctionExpression(node: FunctionExpression): Expression { - if (!isAsyncFunctionLike(node)) { - return node; - } + Debug.assert(hasModifier(node, ModifierFlags.Async)); if (nodeIsMissing(node.body)) { return createOmittedExpression(); } @@ -213,19 +212,19 @@ namespace ts { ); setOriginalNode(func, node); - return func; } /** - * @remarks + * Visits an ArrowFunction. + * * This function will be called when one of the following conditions are met: * - The node is marked async + * + * @param node The node to visit. */ function visitArrowFunction(node: ArrowFunction) { - if (!isAsyncFunctionLike(node)) { - return node; - } + Debug.assert(hasModifier(node, ModifierFlags.Async)); const func = createArrowFunction( visitNodes(node.modifiers, visitor, isModifier), /*typeParameters*/ undefined, @@ -237,7 +236,6 @@ namespace ts { ); setOriginalNode(func, node); - return func; } @@ -280,7 +278,7 @@ namespace ts { statements.push( createReturn( createAwaiterHelper( - currentSourceFileExternalHelpersModuleName, + helperState, hasLexicalArguments, promiseConstructor, transformFunctionBodyWorker(node.body, statementOffset) @@ -295,11 +293,11 @@ namespace ts { if (languageVersion >= ScriptTarget.ES2015) { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) { enableSubstitutionForAsyncMethodsWithSuper(); - setEmitFlags(block, EmitFlags.EmitAdvancedSuperHelper); + addEmitHelper(block, advancedAsyncSuperHelper); } else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) { enableSubstitutionForAsyncMethodsWithSuper(); - setEmitFlags(block, EmitFlags.EmitSuperHelper); + addEmitHelper(block, asyncSuperHelper); } } @@ -307,7 +305,7 @@ namespace ts { } else { return createAwaiterHelper( - currentSourceFileExternalHelpersModuleName, + helperState, hasLexicalArguments, promiseConstructor, transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ true) @@ -507,4 +505,63 @@ namespace ts { && resolver.getNodeCheckFlags(currentSuperContainer) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding); } } + + function createAwaiterHelper(helperState: EmitHelperState, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { + const generatorFunc = createFunctionExpression( + /*modifiers*/ undefined, + createToken(SyntaxKind.AsteriskToken), + /*name*/ undefined, + /*typeParameters*/ undefined, + /*parameters*/ [], + /*type*/ undefined, + body + ); + + // Mark this node as originally an async function + (generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody; + + requestEmitHelper(helperState, awaiterHelper); + return createCall( + getHelperName(helperState, "__awaiter"), + /*typeArguments*/ undefined, + [ + createThis(), + hasLexicalArguments ? createIdentifier("arguments") : createVoidZero(), + promiseConstructor ? createExpressionFromEntityName(promiseConstructor) : createVoidZero(), + generatorFunc + ] + ); + } + + const awaiterHelper: EmitHelper = { + name: "typescript:awaiter", + scoped: false, + priority: 5, + text: ` + var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); + };` + }; + + const asyncSuperHelper: EmitHelper = { + name: "typescript:async-super", + scoped: true, + text: ` + const _super = name => super[name];` + }; + + const advancedAsyncSuperHelper: EmitHelper = { + name: "typescript:advanced-async-super", + scoped: true, + text: ` + const _super = (function (geti, seti) { + const cache = Object.create(null); + return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); + })(name => super[name], (name, value) => super[name] = value);` + }; } diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 7c9cde59fde..78c9e42c1bf 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -242,6 +242,7 @@ namespace ts { let currentSourceFile: SourceFile; let renamedCatchVariables: Map; let renamedCatchVariableDeclarations: Map; + let helperState: EmitHelperState; let inGeneratorFunctionBody: boolean; let inStatementContainingYield: boolean; @@ -291,17 +292,20 @@ namespace ts { return transformSourceFile; function transformSourceFile(node: SourceFile) { - if (isDeclarationFile(node)) { + if (isDeclarationFile(node) + || (node.transformFlags & TransformFlags.ContainsGenerator) === 0) { return node; } - if (node.transformFlags & TransformFlags.ContainsGenerator) { - currentSourceFile = node; - node = visitEachChild(node, visitor, context); - currentSourceFile = undefined; - } + currentSourceFile = node; + helperState = { currentSourceFile, compilerOptions }; - return node; + const visited = visitEachChild(node, visitor, context); + addEmitHelpers(visited, helperState.requestedHelpers); + + currentSourceFile = undefined; + helperState = undefined; + return visited; } /** @@ -2585,28 +2589,24 @@ namespace ts { withBlockStack = undefined; const buildResult = buildStatements(); - return createCall( - createHelperName(currentSourceFile.externalHelpersModuleName, "__generator"), - /*typeArguments*/ undefined, - [ - createThis(), - setEmitFlags( - createFunctionExpression( - /*modifiers*/ undefined, - /*asteriskToken*/ undefined, - /*name*/ undefined, - /*typeParameters*/ undefined, - [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, state)], - /*type*/ undefined, - createBlock( - buildResult, - /*location*/ undefined, - /*multiLine*/ buildResult.length > 0 - ) - ), - EmitFlags.ReuseTempVariableScope - ) - ] + return createGeneratorHelper( + helperState, + setEmitFlags( + createFunctionExpression( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + /*name*/ undefined, + /*typeParameters*/ undefined, + [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, state)], + /*type*/ undefined, + createBlock( + buildResult, + /*location*/ undefined, + /*multiLine*/ buildResult.length > 0 + ) + ), + EmitFlags.ReuseTempVariableScope + ) ); } @@ -3082,4 +3082,105 @@ namespace ts { ); } } + + function createGeneratorHelper(helperState: EmitHelperState, body: FunctionExpression) { + requestEmitHelper(helperState, generatorHelper); + return createCall( + getHelperName(helperState, "__generator"), + /*typeArguments*/ undefined, + [createThis(), body]); + } + + // The __generator helper is used by down-level transformations to emulate the runtime + // semantics of an ES2015 generator function. When called, this helper returns an + // object that implements the Iterator protocol, in that it has `next`, `return`, and + // `throw` methods that step through the generator when invoked. + // + // parameters: + // thisArg The value to use as the `this` binding for the transformed generator body. + // body A function that acts as the transformed generator body. + // + // variables: + // _ Persistent state for the generator that is shared between the helper and the + // generator body. The state object has the following members: + // sent() - A method that returns or throws the current completion value. + // label - The next point at which to resume evaluation of the generator body. + // trys - A stack of protected regions (try/catch/finally blocks). + // ops - A stack of pending instructions when inside of a finally block. + // f A value indicating whether the generator is executing. + // y An iterator to delegate for a yield*. + // t A temporary variable that holds one of the following values (note that these + // cases do not overlap): + // - The completion value when resuming from a `yield` or `yield*`. + // - The error value for a catch block. + // - The current protected region (array of try/catch/finally/end labels). + // - The verb (`next`, `throw`, or `return` method) to delegate to the expression + // of a `yield*`. + // - The result of evaluating the verb delegated to the expression of a `yield*`. + // + // functions: + // verb(n) Creates a bound callback to the `step` function for opcode `n`. + // step(op) Evaluates opcodes in a generator body until execution is suspended or + // completed. + // + // The __generator helper understands a limited set of instructions: + // 0: next(value?) - Start or resume the generator with the specified value. + // 1: throw(error) - Resume the generator with an exception. If the generator is + // suspended inside of one or more protected regions, evaluates + // any intervening finally blocks between the current label and + // the nearest catch block or function boundary. If uncaught, the + // exception is thrown to the caller. + // 2: return(value?) - Resume the generator as if with a return. If the generator is + // suspended inside of one or more protected regions, evaluates any + // intervening finally blocks. + // 3: break(label) - Jump to the specified label. If the label is outside of the + // current protected region, evaluates any intervening finally + // blocks. + // 4: yield(value?) - Yield execution to the caller with an optional value. When + // resumed, the generator will continue at the next label. + // 5: yield*(value) - Delegates evaluation to the supplied iterator. When + // delegation completes, the generator will continue at the next + // label. + // 6: catch(error) - Handles an exception thrown from within the generator body. If + // the current label is inside of one or more protected regions, + // evaluates any intervening finally blocks between the current + // label and the nearest catch block or function boundary. If + // uncaught, the exception is thrown to the caller. + // 7: endfinally - Ends a finally block, resuming the last instruction prior to + // entering a finally block. + // + // For examples of how these are used, see the comments in ./transformers/generators.ts + const generatorHelper: EmitHelper = { + name: "typescript:generator", + scoped: false, + priority: 6, + text: ` + var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t; + return { next: verb(0), "throw": verb(1), "return": verb(2) }; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } + };` + }; } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 95a4016bb0a..8867896e51e 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -3,11 +3,11 @@ /*@internal*/ namespace ts { - const entities: Map = createEntitiesMap(); - export function transformJsx(context: TransformationContext) { const compilerOptions = context.getCompilerOptions(); let currentSourceFile: SourceFile; + let helperState: EmitHelperState; + return transformSourceFile; /** @@ -21,9 +21,14 @@ namespace ts { } currentSourceFile = node; - node = visitEachChild(node, visitor, context); + helperState = { currentSourceFile, compilerOptions }; + + const visited = visitEachChild(node, visitor, context); + addEmitHelpers(visited, helperState.requestedHelpers); + currentSourceFile = undefined; - return node; + helperState = undefined; + return visited; } function visitor(node: Node): VisitResult { @@ -109,8 +114,10 @@ namespace ts { // Either emit one big object literal (no spread attribs), or // a call to the __assign helper. - objectProperties = singleOrUndefined(segments) - || createAssignHelper(currentSourceFile.externalHelpersModuleName, segments); + objectProperties = singleOrUndefined(segments); + if (!objectProperties) { + objectProperties = createAssignHelper(helperState, segments); + } } const element = createReactCreateElement( @@ -275,261 +282,283 @@ namespace ts { } } - function createEntitiesMap(): Map { - return createMap({ - "quot": 0x0022, - "amp": 0x0026, - "apos": 0x0027, - "lt": 0x003C, - "gt": 0x003E, - "nbsp": 0x00A0, - "iexcl": 0x00A1, - "cent": 0x00A2, - "pound": 0x00A3, - "curren": 0x00A4, - "yen": 0x00A5, - "brvbar": 0x00A6, - "sect": 0x00A7, - "uml": 0x00A8, - "copy": 0x00A9, - "ordf": 0x00AA, - "laquo": 0x00AB, - "not": 0x00AC, - "shy": 0x00AD, - "reg": 0x00AE, - "macr": 0x00AF, - "deg": 0x00B0, - "plusmn": 0x00B1, - "sup2": 0x00B2, - "sup3": 0x00B3, - "acute": 0x00B4, - "micro": 0x00B5, - "para": 0x00B6, - "middot": 0x00B7, - "cedil": 0x00B8, - "sup1": 0x00B9, - "ordm": 0x00BA, - "raquo": 0x00BB, - "frac14": 0x00BC, - "frac12": 0x00BD, - "frac34": 0x00BE, - "iquest": 0x00BF, - "Agrave": 0x00C0, - "Aacute": 0x00C1, - "Acirc": 0x00C2, - "Atilde": 0x00C3, - "Auml": 0x00C4, - "Aring": 0x00C5, - "AElig": 0x00C6, - "Ccedil": 0x00C7, - "Egrave": 0x00C8, - "Eacute": 0x00C9, - "Ecirc": 0x00CA, - "Euml": 0x00CB, - "Igrave": 0x00CC, - "Iacute": 0x00CD, - "Icirc": 0x00CE, - "Iuml": 0x00CF, - "ETH": 0x00D0, - "Ntilde": 0x00D1, - "Ograve": 0x00D2, - "Oacute": 0x00D3, - "Ocirc": 0x00D4, - "Otilde": 0x00D5, - "Ouml": 0x00D6, - "times": 0x00D7, - "Oslash": 0x00D8, - "Ugrave": 0x00D9, - "Uacute": 0x00DA, - "Ucirc": 0x00DB, - "Uuml": 0x00DC, - "Yacute": 0x00DD, - "THORN": 0x00DE, - "szlig": 0x00DF, - "agrave": 0x00E0, - "aacute": 0x00E1, - "acirc": 0x00E2, - "atilde": 0x00E3, - "auml": 0x00E4, - "aring": 0x00E5, - "aelig": 0x00E6, - "ccedil": 0x00E7, - "egrave": 0x00E8, - "eacute": 0x00E9, - "ecirc": 0x00EA, - "euml": 0x00EB, - "igrave": 0x00EC, - "iacute": 0x00ED, - "icirc": 0x00EE, - "iuml": 0x00EF, - "eth": 0x00F0, - "ntilde": 0x00F1, - "ograve": 0x00F2, - "oacute": 0x00F3, - "ocirc": 0x00F4, - "otilde": 0x00F5, - "ouml": 0x00F6, - "divide": 0x00F7, - "oslash": 0x00F8, - "ugrave": 0x00F9, - "uacute": 0x00FA, - "ucirc": 0x00FB, - "uuml": 0x00FC, - "yacute": 0x00FD, - "thorn": 0x00FE, - "yuml": 0x00FF, - "OElig": 0x0152, - "oelig": 0x0153, - "Scaron": 0x0160, - "scaron": 0x0161, - "Yuml": 0x0178, - "fnof": 0x0192, - "circ": 0x02C6, - "tilde": 0x02DC, - "Alpha": 0x0391, - "Beta": 0x0392, - "Gamma": 0x0393, - "Delta": 0x0394, - "Epsilon": 0x0395, - "Zeta": 0x0396, - "Eta": 0x0397, - "Theta": 0x0398, - "Iota": 0x0399, - "Kappa": 0x039A, - "Lambda": 0x039B, - "Mu": 0x039C, - "Nu": 0x039D, - "Xi": 0x039E, - "Omicron": 0x039F, - "Pi": 0x03A0, - "Rho": 0x03A1, - "Sigma": 0x03A3, - "Tau": 0x03A4, - "Upsilon": 0x03A5, - "Phi": 0x03A6, - "Chi": 0x03A7, - "Psi": 0x03A8, - "Omega": 0x03A9, - "alpha": 0x03B1, - "beta": 0x03B2, - "gamma": 0x03B3, - "delta": 0x03B4, - "epsilon": 0x03B5, - "zeta": 0x03B6, - "eta": 0x03B7, - "theta": 0x03B8, - "iota": 0x03B9, - "kappa": 0x03BA, - "lambda": 0x03BB, - "mu": 0x03BC, - "nu": 0x03BD, - "xi": 0x03BE, - "omicron": 0x03BF, - "pi": 0x03C0, - "rho": 0x03C1, - "sigmaf": 0x03C2, - "sigma": 0x03C3, - "tau": 0x03C4, - "upsilon": 0x03C5, - "phi": 0x03C6, - "chi": 0x03C7, - "psi": 0x03C8, - "omega": 0x03C9, - "thetasym": 0x03D1, - "upsih": 0x03D2, - "piv": 0x03D6, - "ensp": 0x2002, - "emsp": 0x2003, - "thinsp": 0x2009, - "zwnj": 0x200C, - "zwj": 0x200D, - "lrm": 0x200E, - "rlm": 0x200F, - "ndash": 0x2013, - "mdash": 0x2014, - "lsquo": 0x2018, - "rsquo": 0x2019, - "sbquo": 0x201A, - "ldquo": 0x201C, - "rdquo": 0x201D, - "bdquo": 0x201E, - "dagger": 0x2020, - "Dagger": 0x2021, - "bull": 0x2022, - "hellip": 0x2026, - "permil": 0x2030, - "prime": 0x2032, - "Prime": 0x2033, - "lsaquo": 0x2039, - "rsaquo": 0x203A, - "oline": 0x203E, - "frasl": 0x2044, - "euro": 0x20AC, - "image": 0x2111, - "weierp": 0x2118, - "real": 0x211C, - "trade": 0x2122, - "alefsym": 0x2135, - "larr": 0x2190, - "uarr": 0x2191, - "rarr": 0x2192, - "darr": 0x2193, - "harr": 0x2194, - "crarr": 0x21B5, - "lArr": 0x21D0, - "uArr": 0x21D1, - "rArr": 0x21D2, - "dArr": 0x21D3, - "hArr": 0x21D4, - "forall": 0x2200, - "part": 0x2202, - "exist": 0x2203, - "empty": 0x2205, - "nabla": 0x2207, - "isin": 0x2208, - "notin": 0x2209, - "ni": 0x220B, - "prod": 0x220F, - "sum": 0x2211, - "minus": 0x2212, - "lowast": 0x2217, - "radic": 0x221A, - "prop": 0x221D, - "infin": 0x221E, - "ang": 0x2220, - "and": 0x2227, - "or": 0x2228, - "cap": 0x2229, - "cup": 0x222A, - "int": 0x222B, - "there4": 0x2234, - "sim": 0x223C, - "cong": 0x2245, - "asymp": 0x2248, - "ne": 0x2260, - "equiv": 0x2261, - "le": 0x2264, - "ge": 0x2265, - "sub": 0x2282, - "sup": 0x2283, - "nsub": 0x2284, - "sube": 0x2286, - "supe": 0x2287, - "oplus": 0x2295, - "otimes": 0x2297, - "perp": 0x22A5, - "sdot": 0x22C5, - "lceil": 0x2308, - "rceil": 0x2309, - "lfloor": 0x230A, - "rfloor": 0x230B, - "lang": 0x2329, - "rang": 0x232A, - "loz": 0x25CA, - "spades": 0x2660, - "clubs": 0x2663, - "hearts": 0x2665, - "diams": 0x2666 - }); + const entities = createMap({ + "quot": 0x0022, + "amp": 0x0026, + "apos": 0x0027, + "lt": 0x003C, + "gt": 0x003E, + "nbsp": 0x00A0, + "iexcl": 0x00A1, + "cent": 0x00A2, + "pound": 0x00A3, + "curren": 0x00A4, + "yen": 0x00A5, + "brvbar": 0x00A6, + "sect": 0x00A7, + "uml": 0x00A8, + "copy": 0x00A9, + "ordf": 0x00AA, + "laquo": 0x00AB, + "not": 0x00AC, + "shy": 0x00AD, + "reg": 0x00AE, + "macr": 0x00AF, + "deg": 0x00B0, + "plusmn": 0x00B1, + "sup2": 0x00B2, + "sup3": 0x00B3, + "acute": 0x00B4, + "micro": 0x00B5, + "para": 0x00B6, + "middot": 0x00B7, + "cedil": 0x00B8, + "sup1": 0x00B9, + "ordm": 0x00BA, + "raquo": 0x00BB, + "frac14": 0x00BC, + "frac12": 0x00BD, + "frac34": 0x00BE, + "iquest": 0x00BF, + "Agrave": 0x00C0, + "Aacute": 0x00C1, + "Acirc": 0x00C2, + "Atilde": 0x00C3, + "Auml": 0x00C4, + "Aring": 0x00C5, + "AElig": 0x00C6, + "Ccedil": 0x00C7, + "Egrave": 0x00C8, + "Eacute": 0x00C9, + "Ecirc": 0x00CA, + "Euml": 0x00CB, + "Igrave": 0x00CC, + "Iacute": 0x00CD, + "Icirc": 0x00CE, + "Iuml": 0x00CF, + "ETH": 0x00D0, + "Ntilde": 0x00D1, + "Ograve": 0x00D2, + "Oacute": 0x00D3, + "Ocirc": 0x00D4, + "Otilde": 0x00D5, + "Ouml": 0x00D6, + "times": 0x00D7, + "Oslash": 0x00D8, + "Ugrave": 0x00D9, + "Uacute": 0x00DA, + "Ucirc": 0x00DB, + "Uuml": 0x00DC, + "Yacute": 0x00DD, + "THORN": 0x00DE, + "szlig": 0x00DF, + "agrave": 0x00E0, + "aacute": 0x00E1, + "acirc": 0x00E2, + "atilde": 0x00E3, + "auml": 0x00E4, + "aring": 0x00E5, + "aelig": 0x00E6, + "ccedil": 0x00E7, + "egrave": 0x00E8, + "eacute": 0x00E9, + "ecirc": 0x00EA, + "euml": 0x00EB, + "igrave": 0x00EC, + "iacute": 0x00ED, + "icirc": 0x00EE, + "iuml": 0x00EF, + "eth": 0x00F0, + "ntilde": 0x00F1, + "ograve": 0x00F2, + "oacute": 0x00F3, + "ocirc": 0x00F4, + "otilde": 0x00F5, + "ouml": 0x00F6, + "divide": 0x00F7, + "oslash": 0x00F8, + "ugrave": 0x00F9, + "uacute": 0x00FA, + "ucirc": 0x00FB, + "uuml": 0x00FC, + "yacute": 0x00FD, + "thorn": 0x00FE, + "yuml": 0x00FF, + "OElig": 0x0152, + "oelig": 0x0153, + "Scaron": 0x0160, + "scaron": 0x0161, + "Yuml": 0x0178, + "fnof": 0x0192, + "circ": 0x02C6, + "tilde": 0x02DC, + "Alpha": 0x0391, + "Beta": 0x0392, + "Gamma": 0x0393, + "Delta": 0x0394, + "Epsilon": 0x0395, + "Zeta": 0x0396, + "Eta": 0x0397, + "Theta": 0x0398, + "Iota": 0x0399, + "Kappa": 0x039A, + "Lambda": 0x039B, + "Mu": 0x039C, + "Nu": 0x039D, + "Xi": 0x039E, + "Omicron": 0x039F, + "Pi": 0x03A0, + "Rho": 0x03A1, + "Sigma": 0x03A3, + "Tau": 0x03A4, + "Upsilon": 0x03A5, + "Phi": 0x03A6, + "Chi": 0x03A7, + "Psi": 0x03A8, + "Omega": 0x03A9, + "alpha": 0x03B1, + "beta": 0x03B2, + "gamma": 0x03B3, + "delta": 0x03B4, + "epsilon": 0x03B5, + "zeta": 0x03B6, + "eta": 0x03B7, + "theta": 0x03B8, + "iota": 0x03B9, + "kappa": 0x03BA, + "lambda": 0x03BB, + "mu": 0x03BC, + "nu": 0x03BD, + "xi": 0x03BE, + "omicron": 0x03BF, + "pi": 0x03C0, + "rho": 0x03C1, + "sigmaf": 0x03C2, + "sigma": 0x03C3, + "tau": 0x03C4, + "upsilon": 0x03C5, + "phi": 0x03C6, + "chi": 0x03C7, + "psi": 0x03C8, + "omega": 0x03C9, + "thetasym": 0x03D1, + "upsih": 0x03D2, + "piv": 0x03D6, + "ensp": 0x2002, + "emsp": 0x2003, + "thinsp": 0x2009, + "zwnj": 0x200C, + "zwj": 0x200D, + "lrm": 0x200E, + "rlm": 0x200F, + "ndash": 0x2013, + "mdash": 0x2014, + "lsquo": 0x2018, + "rsquo": 0x2019, + "sbquo": 0x201A, + "ldquo": 0x201C, + "rdquo": 0x201D, + "bdquo": 0x201E, + "dagger": 0x2020, + "Dagger": 0x2021, + "bull": 0x2022, + "hellip": 0x2026, + "permil": 0x2030, + "prime": 0x2032, + "Prime": 0x2033, + "lsaquo": 0x2039, + "rsaquo": 0x203A, + "oline": 0x203E, + "frasl": 0x2044, + "euro": 0x20AC, + "image": 0x2111, + "weierp": 0x2118, + "real": 0x211C, + "trade": 0x2122, + "alefsym": 0x2135, + "larr": 0x2190, + "uarr": 0x2191, + "rarr": 0x2192, + "darr": 0x2193, + "harr": 0x2194, + "crarr": 0x21B5, + "lArr": 0x21D0, + "uArr": 0x21D1, + "rArr": 0x21D2, + "dArr": 0x21D3, + "hArr": 0x21D4, + "forall": 0x2200, + "part": 0x2202, + "exist": 0x2203, + "empty": 0x2205, + "nabla": 0x2207, + "isin": 0x2208, + "notin": 0x2209, + "ni": 0x220B, + "prod": 0x220F, + "sum": 0x2211, + "minus": 0x2212, + "lowast": 0x2217, + "radic": 0x221A, + "prop": 0x221D, + "infin": 0x221E, + "ang": 0x2220, + "and": 0x2227, + "or": 0x2228, + "cap": 0x2229, + "cup": 0x222A, + "int": 0x222B, + "there4": 0x2234, + "sim": 0x223C, + "cong": 0x2245, + "asymp": 0x2248, + "ne": 0x2260, + "equiv": 0x2261, + "le": 0x2264, + "ge": 0x2265, + "sub": 0x2282, + "sup": 0x2283, + "nsub": 0x2284, + "sube": 0x2286, + "supe": 0x2287, + "oplus": 0x2295, + "otimes": 0x2297, + "perp": 0x22A5, + "sdot": 0x22C5, + "lceil": 0x2308, + "rceil": 0x2309, + "lfloor": 0x230A, + "rfloor": 0x230B, + "lang": 0x2329, + "rang": 0x232A, + "loz": 0x25CA, + "spades": 0x2660, + "clubs": 0x2663, + "hearts": 0x2665, + "diams": 0x2666 + }); + + function createAssignHelper(helperState: EmitHelperState, attributesSegments: Expression[]) { + requestEmitHelper(helperState, assignHelper); + return createCall( + getHelperName(helperState, "__assign"), + /*typeArguments*/ undefined, + attributesSegments + ); } + + const assignHelper: EmitHelper = { + name: "typescript:assign", + scoped: false, + priority: 1, + text: ` + var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + };` + }; } \ No newline at end of file diff --git a/src/compiler/transformers/module/es2015.ts b/src/compiler/transformers/module/es2015.ts index 93aa108617a..daef9cb5a4d 100644 --- a/src/compiler/transformers/module/es2015.ts +++ b/src/compiler/transformers/module/es2015.ts @@ -13,7 +13,27 @@ namespace ts { } if (isExternalModule(node) || compilerOptions.isolatedModules) { - return visitEachChild(node, visitor, context); + const externalHelpersModuleName = getExternalHelpersModuleName(node); + if (externalHelpersModuleName) { + const statements: Statement[] = []; + const statementOffset = addPrologueDirectives(statements, node.statements); + append(statements, + createImportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)), + createLiteral(externalHelpersModuleNameText) + ) + ); + + addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); + return updateSourceFileNode( + node, + createNodeArray(statements, node.statements)); + } + else { + return visitEachChild(node, visitor, context); + } } return node; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 87d02b44025..b946d93f32d 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -82,13 +82,14 @@ namespace ts { const statements: Statement[] = []; const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, sourceElementVisitor); + append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement, /*optional*/ true)); addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset)); addRange(statements, endLexicalEnvironment()); addExportEqualsIfNeeded(statements, /*emitAsReturn*/ false); const updated = updateSourceFileNode(node, createNodeArray(statements, node.statements)); if (currentModuleInfo.hasExportStarsToExportValues) { - setEmitFlags(updated, EmitFlags.EmitExportStar | getEmitFlags(node)); + addEmitHelper(updated, exportStarHelper); } return updated; @@ -257,6 +258,7 @@ namespace ts { const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, sourceElementVisitor); // Visit each statement of the module body. + append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement, /*optional*/ true)); addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset)); // End the lexical environment for the module body @@ -270,7 +272,7 @@ namespace ts { if (currentModuleInfo.hasExportStarsToExportValues) { // If we have any `export * from ...` declarations // we need to inform the emitter to add the __export helper. - setEmitFlags(body, EmitFlags.EmitExportStar); + addEmitHelper(body, exportStarHelper); } return body; @@ -1312,4 +1314,14 @@ namespace ts { } } } + + // emit output for the __export helper function + const exportStarHelper: EmitHelper = { + name: "typescript:export-star", + scoped: true, + text: ` + function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; + }` + }; } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index adc9385935b..4b24d5c1641 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -82,6 +82,7 @@ namespace ts { // Add the body of the module. const dependencyGroups = collectDependencyGroups(moduleInfo.externalImports); + const moduleBodyBlock = createSystemModuleBody(node, dependencyGroups); const moduleBodyFunction = createFunctionExpression( /*modifiers*/ undefined, /*asteriskToken*/ undefined, @@ -92,7 +93,7 @@ namespace ts { createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, contextObject) ], /*type*/ undefined, - createSystemModuleBody(node, dependencyGroups) + moduleBodyBlock ); // Write the call to `System.register` @@ -115,7 +116,9 @@ namespace ts { ], node.statements) ); - setEmitFlags(updated, getEmitFlags(node) & ~EmitFlags.EmitEmitHelpers); + if (!(compilerOptions.outFile || compilerOptions.out)) { + moveEmitHelpers(updated, moduleBodyBlock, helper => !helper.scoped); + } if (noSubstitution) { noSubstitutionMap[id] = noSubstitution; @@ -236,6 +239,9 @@ namespace ts { ) ); + // Visit the synthetic external helpers import declaration if present + visitNode(moduleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement, /*optional*/ true); + // 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 @@ -280,9 +286,7 @@ namespace ts { ) ); - const body = createBlock(statements, /*location*/ undefined, /*multiLine*/ true); - setEmitFlags(body, EmitFlags.EmitEmitHelpers); - return body; + return createBlock(statements, /*location*/ undefined, /*multiLine*/ true); } /** @@ -394,7 +398,13 @@ namespace ts { if (localNames) { condition = createLogicalAnd( condition, - createLogicalNot(createHasOwnProperty(localNames, n)) + createLogicalNot( + createCall( + createPropertyAccess(localNames, "hasOwnProperty"), + /*typeArguments*/ undefined, + [n] + ) + ) ); } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index fe264876b25..02c7b107b25 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -48,7 +48,7 @@ namespace ts { let currentNamespaceContainerName: Identifier; let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; let currentScopeFirstDeclarationsOfName: Map; - let currentExternalHelpersModuleName: Identifier; + let helperState: EmitHelperState; /** * Keeps track of whether expression substitution has been enabled for specific edge cases. @@ -80,7 +80,23 @@ namespace ts { return node; } - return visitNode(node, visitor, isSourceFile); + currentSourceFile = node; + currentScope = node; + currentScopeFirstDeclarationsOfName = createMap(); + helperState = { currentSourceFile, compilerOptions }; + + let visited = visitEachChild(node, sourceElementVisitor, context); + if (compilerOptions.alwaysStrict) { + visited = updateSourceFileNode(visited, ensureUseStrict(visited.statements)); + } + + addEmitHelpers(visited, helperState.requestedHelpers); + + currentSourceFile = undefined; + currentScope = undefined; + currentScopeFirstDeclarationsOfName = undefined; + helperState = undefined; + return visited; } /** @@ -122,10 +138,7 @@ namespace ts { * @param node The node to visit. */ function visitorWorker(node: Node): VisitResult { - if (node.kind === SyntaxKind.SourceFile) { - return visitSourceFile(node); - } - else if (node.transformFlags & TransformFlags.TypeScript) { + if (node.transformFlags & TransformFlags.TypeScript) { // This node is explicitly marked as TypeScript, so we should transform the node. return visitTypeScript(node); } @@ -252,7 +265,6 @@ namespace ts { return node; } - /** * Branching visitor, visits a TypeScript syntax node. * @@ -446,7 +458,6 @@ namespace ts { */ function onBeforeVisitNode(node: Node) { switch (node.kind) { - case SyntaxKind.SourceFile: case SyntaxKind.CaseBlock: case SyntaxKind.ModuleBlock: case SyntaxKind.Block: @@ -465,50 +476,6 @@ namespace ts { } } - function visitSourceFile(node: SourceFile) { - currentSourceFile = node; - - // ensure "use strict" is emitted in all scenarios in alwaysStrict mode - if (compilerOptions.alwaysStrict) { - node = ensureUseStrict(node); - } - - // If the source file requires any helpers and is an external module, and - // the importHelpers compiler option is enabled, emit a synthesized import - // statement for the helpers library. - if (node.flags & NodeFlags.EmitHelperFlags - && compilerOptions.importHelpers - && (isExternalModule(node) || compilerOptions.isolatedModules)) { - startLexicalEnvironment(); - const statements: Statement[] = []; - const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false, visitor); - const externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText); - const externalHelpersModuleImport = createImportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)), - createLiteral(externalHelpersModuleNameText)); - - externalHelpersModuleImport.parent = node; - externalHelpersModuleImport.flags &= ~NodeFlags.Synthesized; - statements.push(externalHelpersModuleImport); - - currentExternalHelpersModuleName = externalHelpersModuleName; - addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset)); - addRange(statements, endLexicalEnvironment()); - currentExternalHelpersModuleName = undefined; - - node = updateSourceFileNode(node, createNodeArray(statements, node.statements)); - node.externalHelpersModuleName = externalHelpersModuleName; - } - else { - node = visitEachChild(node, sourceElementVisitor, context); - } - - setEmitFlags(node, EmitFlags.EmitEmitHelpers | getEmitFlags(node)); - return node; - } - /** * Tests whether we should emit a __decorate call for a class declaration. */ @@ -1418,7 +1385,7 @@ namespace ts { : undefined; const helper = createDecorateHelper( - currentExternalHelpersModuleName, + helperState, decoratorExpressions, prefix, memberName, @@ -1456,7 +1423,7 @@ namespace ts { const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; const localName = getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); - const decorate = createDecorateHelper(currentExternalHelpersModuleName, decoratorExpressions, localName); + const decorate = createDecorateHelper(helperState, decoratorExpressions, localName); const expression = createAssignment(localName, classAlias ? createAssignment(classAlias, decorate) : decorate); setEmitFlags(expression, EmitFlags.NoComments); setSourceMapRange(expression, moveRangePastDecorators(node)); @@ -1484,7 +1451,7 @@ namespace ts { expressions = []; for (const decorator of decorators) { const helper = createParamHelper( - currentExternalHelpersModuleName, + helperState, transformDecorator(decorator), parameterOffset, /*location*/ decorator.expression); @@ -1514,13 +1481,13 @@ namespace ts { function addOldTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) { if (compilerOptions.emitDecoratorMetadata) { if (shouldAddTypeMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:type", serializeTypeOfNode(node))); + decoratorExpressions.push(createMetadataHelper(helperState, "design:type", serializeTypeOfNode(node))); } if (shouldAddParamTypesMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:paramtypes", serializeParameterTypesOfNode(node))); + decoratorExpressions.push(createMetadataHelper(helperState, "design:paramtypes", serializeParameterTypesOfNode(node))); } if (shouldAddReturnTypeMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:returntype", serializeReturnTypeOfNode(node))); + decoratorExpressions.push(createMetadataHelper(helperState, "design:returntype", serializeReturnTypeOfNode(node))); } } } @@ -1538,7 +1505,7 @@ namespace ts { (properties || (properties = [])).push(createPropertyAssignment("returnType", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), serializeReturnTypeOfNode(node)))); } if (properties) { - decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true))); + decoratorExpressions.push(createMetadataHelper(helperState, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true))); } } } @@ -3404,4 +3371,77 @@ namespace ts { : undefined; } } + + function createParamHelper(helperState: EmitHelperState, expression: Expression, parameterOffset: number, location?: TextRange) { + requestEmitHelper(helperState, paramHelper); + return createCall( + getHelperName(helperState, "__param"), + /*typeArguments*/ undefined, + [ + createLiteral(parameterOffset), + expression + ], + location + ); + } + + function createMetadataHelper(helperState: EmitHelperState, metadataKey: string, metadataValue: Expression) { + requestEmitHelper(helperState, metadataHelper); + return createCall( + getHelperName(helperState, "__metadata"), + /*typeArguments*/ undefined, + [ + createLiteral(metadataKey), + metadataValue + ] + ); + } + + function createDecorateHelper(helperState: EmitHelperState, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) { + const argumentsArray: Expression[] = []; + argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true)); + argumentsArray.push(target); + if (memberName) { + argumentsArray.push(memberName); + if (descriptor) { + argumentsArray.push(descriptor); + } + } + + requestEmitHelper(helperState, decorateHelper); + return createCall(getHelperName(helperState, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location); + } + + const decorateHelper: EmitHelper = { + name: "typescript:decorate", + scoped: false, + priority: 2, + text: ` + var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + };` + }; + + const metadataHelper: EmitHelper = { + name: "typescript:metadata", + scoped: false, + priority: 3, + text: ` + var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + };` + }; + + const paramHelper: EmitHelper = { + name: "typescript:param", + scoped: false, + priority: 4, + text: ` + var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } + };` + }; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cfe7cc97ec5..d8043d1fc01 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2088,8 +2088,6 @@ namespace ts { /* @internal */ imports: LiteralExpression[]; /* @internal */ moduleAugmentations: LiteralExpression[]; /* @internal */ patternAmbientModules?: PatternAmbientModule[]; - // The synthesized identifier for an imported external helpers module. - /* @internal */ externalHelpersModuleName?: Identifier; } export interface ScriptReferenceHost { @@ -3476,20 +3474,18 @@ namespace ts { /* @internal */ export interface EmitNode { - flags?: EmitFlags; - commentRange?: TextRange; - sourceMapRange?: TextRange; - tokenSourceMapRanges?: Map; annotatedNodes?: Node[]; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup. - constantValue?: number; + flags?: EmitFlags; // Flags that customize emit + commentRange?: TextRange; // The text range to use when emitting leading or trailing comments + sourceMapRange?: TextRange; // The text range to use when emitting leading or trailing source mappings + tokenSourceMapRanges?: Map; // The text range to use when emitting source mappings for tokens + constantValue?: number; // The constant value of an expression + externalHelpersModuleName?: Identifier; // The local name for an imported helpers module + helpers?: EmitHelper[]; // Emit helpers for the node } /* @internal */ export const enum EmitFlags { - 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. SingleLine = 1 << 5, // The contents of this node should be emitted on a single line. AdviseOnEmitNode = 1 << 6, // The printer should invoke the onEmitNode callback when printing this node. @@ -3517,6 +3513,14 @@ namespace ts { HasEndOfDeclarationMarker = 1 << 25, // Declaration has an associated NotEmittedStatement to mark the end of the declaration } + /* @internal */ + export interface EmitHelper { + readonly name: string; // A unique name for this helper. + readonly scoped: boolean; // Indicates whether ther helper MUST be emitted in the current scope. + readonly text: string; // ES3-compatible raw script text. + readonly priority?: number; // Helpers with a higher priority are emitted earlier than other helpers on the node. + } + /* @internal */ export const enum EmitContext { SourceFile, // Emitting a SourceFile diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index bec92b9a5ca..44e2d4b1135 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3493,153 +3493,6 @@ namespace ts { return positionIsSynthesized(range.pos) ? -1 : skipTrivia(sourceFile.text, range.pos); } - export interface ExternalModuleInfo { - externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules - exportSpecifiers: Map; // export specifiers by name - exportedBindings: Map; // exported names of local declarations - exportedNames: Identifier[]; // all exported names local to module - exportEquals: ExportAssignment | undefined; // an export= declaration if one was present - hasExportStarsToExportValues: boolean; // whether this module contains export* - } - - export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver): ExternalModuleInfo { - const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; - const exportSpecifiers = createMap(); - const exportedBindings = createMap(); - const uniqueExports = createMap(); - let hasExportDefault = false; - let exportEquals: ExportAssignment = undefined; - let hasExportStarsToExportValues = false; - for (const node of sourceFile.statements) { - switch (node.kind) { - case SyntaxKind.ImportDeclaration: - // import "mod" - // import x from "mod" - // import * as x from "mod" - // import { x, y } from "mod" - externalImports.push(node); - break; - - case SyntaxKind.ImportEqualsDeclaration: - if ((node).moduleReference.kind === SyntaxKind.ExternalModuleReference) { - // import x = require("mod") - externalImports.push(node); - } - - break; - - case SyntaxKind.ExportDeclaration: - if ((node).moduleSpecifier) { - if (!(node).exportClause) { - // export * from "mod" - externalImports.push(node); - hasExportStarsToExportValues = true; - } - else { - // export { x, y } from "mod" - externalImports.push(node); - } - } - else { - // export { x, y } - for (const specifier of (node).exportClause.elements) { - if (!uniqueExports[specifier.name.text]) { - const name = specifier.propertyName || specifier.name; - multiMapAdd(exportSpecifiers, name.text, specifier); - - const decl = resolver.getReferencedImportDeclaration(name) - || resolver.getReferencedValueDeclaration(name); - - if (decl) { - multiMapAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); - } - - uniqueExports[specifier.name.text] = specifier.name; - } - } - } - break; - - case SyntaxKind.ExportAssignment: - if ((node).isExportEquals && !exportEquals) { - // export = x - exportEquals = node; - } - break; - - case SyntaxKind.VariableStatement: - if (hasModifier(node, ModifierFlags.Export)) { - for (const decl of (node).declarationList.declarations) { - collectExportedVariableInfo(decl, uniqueExports); - } - } - break; - - case SyntaxKind.FunctionDeclaration: - if (hasModifier(node, ModifierFlags.Export)) { - if (hasModifier(node, ModifierFlags.Default)) { - // export default function() { } - if (!hasExportDefault) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); - hasExportDefault = true; - } - } - else { - // export function x() { } - const name = (node).name; - if (!uniqueExports[name.text]) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), name); - uniqueExports[name.text] = name; - } - } - } - break; - - case SyntaxKind.ClassDeclaration: - if (hasModifier(node, ModifierFlags.Export)) { - if (hasModifier(node, ModifierFlags.Default)) { - // export default class { } - if (!hasExportDefault) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); - hasExportDefault = true; - } - } - else { - // export class x { } - const name = (node).name; - if (!uniqueExports[name.text]) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), name); - uniqueExports[name.text] = name; - } - } - } - break; - } - } - - let exportedNames: Identifier[]; - for (const key in uniqueExports) { - exportedNames = ts.append(exportedNames, uniqueExports[key]); - } - - return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames }; - } - - function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map) { - if (isBindingPattern(decl.name)) { - for (const element of decl.name.elements) { - if (!isOmittedExpression(element)) { - collectExportedVariableInfo(element, uniqueExports); - } - } - } - else if (!isGeneratedIdentifier(decl.name)) { - if (!uniqueExports[decl.name.text]) { - uniqueExports[decl.name.text] = decl.name; - } - } - } - /** * Determines whether a name was originally the declaration name of an enum or namespace * declaration. From c1505a0ed9b1281ac64955e7359406f53bc39386 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 26 Oct 2016 18:38:59 -0700 Subject: [PATCH 02/26] Added RawExpression to move the UMD helper out of the emitter --- src/compiler/emitter.ts | 21 ++------ src/compiler/factory.ts | 15 +++++- src/compiler/transformers/module/module.ts | 14 +++++- src/compiler/types.ts | 56 +++++++++++++--------- src/compiler/utilities.ts | 5 +- 5 files changed, 66 insertions(+), 45 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ed16ec06159..a4096d91574 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -20,18 +20,6 @@ namespace ts { export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean): EmitResult { const delimiters = createDelimiterMap(); const brackets = createBracketsMap(); - - // emit output for the UMD helper function. - const umdHelper = ` -(function (dependencies, factory) { - if (typeof module === 'object' && typeof module.exports === 'object') { - var v = factory(require, exports); if (v !== undefined) module.exports = v; - } - else if (typeof define === 'function' && define.amd) { - define(dependencies, factory); - } -})`; - const compilerOptions = host.getCompilerOptions(); const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); @@ -674,6 +662,8 @@ namespace ts { // Transformation nodes case SyntaxKind.PartiallyEmittedExpression: return emitPartiallyEmittedExpression(node); + case SyntaxKind.RawExpression: + return writeLines((node).text); } } @@ -711,12 +701,7 @@ namespace ts { // function emitIdentifier(node: Identifier) { - if (getEmitFlags(node) & EmitFlags.UMDDefine) { - writeLines(umdHelper); - } - else { - write(getTextOfNode(node, /*includeTrivia*/ false)); - } + write(getTextOfNode(node, /*includeTrivia*/ false)); } // diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 04b7079d094..046e6f24c26 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1504,6 +1504,19 @@ namespace ts { return node; } + /** + * Creates a node that emits a string of raw text in an expression position. Raw text is never + * transformed, should be ES3 compliant, and should have the same precedence as + * PrimaryExpression. + * + * @param text The raw text of the node. + */ + export function createRawExpression(text: string) { + const node = createNode(SyntaxKind.RawExpression); + node.text = text; + return node; + } + // Compound nodes export function createComma(left: Expression, right: Expression) { @@ -1621,7 +1634,7 @@ namespace ts { // flag and setting a parent node. const react = createIdentifier(reactNamespace || "React"); react.flags &= ~NodeFlags.Synthesized; - // Set the parent that is in parse tree + // Set the parent that is in parse tree // this makes sure that parent chain is intact for checker to traverse complete scope tree react.parent = getParseTreeNode(parent); return react; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index b946d93f32d..22e4e844a69 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -112,8 +112,7 @@ namespace ts { * @param node The SourceFile node. */ function transformUMDModule(node: SourceFile) { - const define = createIdentifier("define"); - setEmitFlags(define, EmitFlags.UMDDefine); + const define = createRawExpression(umdHelper); return transformAsynchronousModule(node, define, /*moduleName*/ undefined, /*includeNonAmdDependencies*/ false); } @@ -1324,4 +1323,15 @@ namespace ts { for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; }` }; + + // emit output for the UMD helper function. + const umdHelper = ` + (function (dependencies, factory) { + if (typeof module === 'object' && typeof module.exports === 'object') { + var v = factory(require, exports); if (v !== undefined) module.exports = v; + } + else if (typeof define === 'function' && define.amd) { + define(dependencies, factory); + } + })`; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d8043d1fc01..526c4e15cb8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -364,6 +364,7 @@ namespace ts { PartiallyEmittedExpression, MergeDeclarationMarker, EndOfDeclarationMarker, + RawExpression, // Enum value count Count, @@ -423,7 +424,7 @@ namespace ts { ThisNodeHasError = 1 << 19, // If the parser encountered an error when parsing the code that created this node JavaScriptFile = 1 << 20, // If node was parsed in a JavaScript ThisNodeOrAnySubNodesHasError = 1 << 21, // If this node or any of its children had an error - HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node + HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node BlockScoped = Let | Const, @@ -1453,6 +1454,16 @@ namespace ts { kind: SyntaxKind.EndOfDeclarationMarker; } + /** + * Emits a string of raw text in an expression position. Raw text is never transformed, should + * be ES3 compliant, and should have the same precedence as PrimaryExpression. + */ + /* @internal */ + export interface RawExpression extends PrimaryExpression { + kind: SyntaxKind.RawExpression; + text: string; + } + /** * Marks the beginning of a merged transformed declaration. */ @@ -3486,31 +3497,30 @@ namespace ts { /* @internal */ export const enum EmitFlags { - UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. - SingleLine = 1 << 5, // The contents of this node should be emitted on a single line. - AdviseOnEmitNode = 1 << 6, // The printer should invoke the onEmitNode callback when printing this node. - NoSubstitution = 1 << 7, // Disables further substitution of an expression. - CapturesThis = 1 << 8, // The function captures a lexical `this` - NoLeadingSourceMap = 1 << 9, // Do not emit a leading source map location for this node. - NoTrailingSourceMap = 1 << 10, // Do not emit a trailing source map location for this node. + SingleLine = 1 << 0, // The contents of this node should be emitted on a single line. + AdviseOnEmitNode = 1 << 1, // The printer should invoke the onEmitNode callback when printing this node. + NoSubstitution = 1 << 2, // Disables further substitution of an expression. + CapturesThis = 1 << 3, // The function captures a lexical `this` + NoLeadingSourceMap = 1 << 4, // Do not emit a leading source map location for this node. + NoTrailingSourceMap = 1 << 5, // Do not emit a trailing source map location for this node. NoSourceMap = NoLeadingSourceMap | NoTrailingSourceMap, // Do not emit a source map location for this node. - NoNestedSourceMaps = 1 << 11, // Do not emit source map locations for children of this node. - NoTokenLeadingSourceMaps = 1 << 12, // Do not emit leading source map location for token nodes. - NoTokenTrailingSourceMaps = 1 << 13, // Do not emit trailing source map location for token nodes. + NoNestedSourceMaps = 1 << 6, // Do not emit source map locations for children of this node. + NoTokenLeadingSourceMaps = 1 << 7, // Do not emit leading source map location for token nodes. + NoTokenTrailingSourceMaps = 1 << 8, // Do not emit trailing source map location for token nodes. NoTokenSourceMaps = NoTokenLeadingSourceMaps | NoTokenTrailingSourceMaps, // Do not emit source map locations for tokens of this node. - NoLeadingComments = 1 << 14, // Do not emit leading comments for this node. - NoTrailingComments = 1 << 15, // Do not emit trailing comments for this node. + NoLeadingComments = 1 << 9, // Do not emit leading comments for this node. + NoTrailingComments = 1 << 10, // Do not emit trailing comments for this node. NoComments = NoLeadingComments | NoTrailingComments, // Do not emit comments for this node. - NoNestedComments = 1 << 16, - ExportName = 1 << 17, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). - LocalName = 1 << 18, // Ensure an export prefix is not added for an identifier that points to an exported declaration. - Indented = 1 << 19, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). - NoIndentation = 1 << 20, // Do not indent the node. - AsyncFunctionBody = 1 << 21, - ReuseTempVariableScope = 1 << 22, // Reuse the existing temp variable scope during emit. - CustomPrologue = 1 << 23, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). - NoHoisting = 1 << 24, // Do not hoist this declaration in --module system - HasEndOfDeclarationMarker = 1 << 25, // Declaration has an associated NotEmittedStatement to mark the end of the declaration + NoNestedComments = 1 << 11, + ExportName = 1 << 12, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). + LocalName = 1 << 13, // Ensure an export prefix is not added for an identifier that points to an exported declaration. + Indented = 1 << 14, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). + NoIndentation = 1 << 15, // Do not indent the node. + AsyncFunctionBody = 1 << 16, + ReuseTempVariableScope = 1 << 17, // Reuse the existing temp variable scope during emit. + CustomPrologue = 1 << 18, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). + NoHoisting = 1 << 19, // Do not hoist this declaration in --module system + HasEndOfDeclarationMarker = 1 << 20, // Declaration has an associated NotEmittedStatement to mark the end of the declaration } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 44e2d4b1135..0ea82dfceb4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2087,6 +2087,7 @@ namespace ts { case SyntaxKind.TemplateExpression: case SyntaxKind.ParenthesizedExpression: case SyntaxKind.OmittedExpression: + case SyntaxKind.RawExpression: return 19; case SyntaxKind.TaggedTemplateExpression: @@ -3796,7 +3797,8 @@ namespace ts { || kind === SyntaxKind.ThisKeyword || kind === SyntaxKind.TrueKeyword || kind === SyntaxKind.SuperKeyword - || kind === SyntaxKind.NonNullExpression; + || kind === SyntaxKind.NonNullExpression + || kind === SyntaxKind.RawExpression; } export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { @@ -3826,6 +3828,7 @@ namespace ts { || kind === SyntaxKind.SpreadElementExpression || kind === SyntaxKind.AsExpression || kind === SyntaxKind.OmittedExpression + || kind === SyntaxKind.RawExpression || isUnaryExpressionKind(kind); } From 12cd0bfb69b88fffc5135a4bccd3eb6b6c4c35b1 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 8 Nov 2016 16:32:41 -0800 Subject: [PATCH 03/26] Remove EmitHelperState, general helper cleanup. --- src/compiler/factory.ts | 57 ++-- src/compiler/transformer.ts | 178 +++++------ src/compiler/transformers/es2015.ts | 246 ++++++++------- src/compiler/transformers/es2017.ts | 141 ++++----- src/compiler/transformers/generators.ts | 13 +- src/compiler/transformers/jsx.ts | 13 +- src/compiler/transformers/module/es2015.ts | 60 +++- src/compiler/transformers/module/module.ts | 10 +- src/compiler/transformers/module/system.ts | 10 +- src/compiler/transformers/ts.ts | 345 +++++++++------------ src/compiler/types.ts | 135 +++++++- src/compiler/utilities.ts | 20 +- src/compiler/visitor.ts | 117 ++++--- 13 files changed, 725 insertions(+), 620 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index cd4f4561fd9..f8b71a733cd 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1695,23 +1695,8 @@ namespace ts { // Helpers - export interface EmitHelperState { - currentSourceFile: SourceFile; - compilerOptions: CompilerOptions; - requestedHelpers?: EmitHelper[]; - } - - export function getHelperName(helperState: EmitHelperState, name: string) { - const externalHelpersModuleName = getOrCreateExternalHelpersModuleName(helperState.currentSourceFile, helperState.compilerOptions); - return externalHelpersModuleName - ? createPropertyAccess(externalHelpersModuleName, name) - : createIdentifier(name); - } - - export function requestEmitHelper(helperState: EmitHelperState, helper: EmitHelper) { - if (!contains(helperState.requestedHelpers, helper)) { - helperState.requestedHelpers = append(helperState.requestedHelpers, helper); - } + export function getHelperName(name: string) { + return setEmitFlags(createIdentifier(name), EmitFlags.HelperName | EmitFlags.AdviseOnEmitNode); } export interface CallBinding { @@ -2061,6 +2046,10 @@ namespace ts { // Utilities + export function convertToFunctionBody(node: ConciseBody) { + return isBlock(node) ? node : createBlock([createReturn(node, /*location*/ node)], /*location*/ node); + } + function isUseStrictPrologue(node: ExpressionStatement): boolean { return (node.expression as StringLiteral).text === "use strict"; } @@ -2110,6 +2099,13 @@ namespace ts { return statementOffset; } + export function startsWithUseStrict(statements: Statement[]) { + const firstStatement = firstOrUndefined(statements); + return firstStatement !== undefined + && isPrologueDirective(firstStatement) + && isUseStrictPrologue(firstStatement); + } + /** * Ensures "use strict" directive is added * @@ -2606,7 +2602,7 @@ namespace ts { * * @param node The node. */ - function getOrCreateEmitNode(node: Node) { + export function getOrCreateEmitNode(node: Node) { if (!node.emitNode) { if (isParseTreeNode(node)) { // To avoid holding onto transformation artifacts, we keep track of any @@ -2735,14 +2731,25 @@ namespace ts { return emitNode && emitNode.externalHelpersModuleName; } - export function getOrCreateExternalHelpersModuleName(node: SourceFile, compilerOptions: CompilerOptions) { + export function getOrCreateExternalHelpersModuleNameIfNeeded(node: SourceFile, compilerOptions: CompilerOptions) { if (compilerOptions.importHelpers && (isExternalModule(node) || compilerOptions.isolatedModules)) { - const parseNode = getOriginalNode(node, isSourceFile); - const emitNode = getOrCreateEmitNode(parseNode); - return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText)); + const externalHelpersModuleName = getExternalHelpersModuleName(node); + if (externalHelpersModuleName) { + return externalHelpersModuleName; + } + + const helpers = getEmitHelpers(node); + if (helpers) { + for (const helper of helpers) { + if (!helper.scoped) { + const parseNode = getOriginalNode(node, isSourceFile); + const emitNode = getOrCreateEmitNode(parseNode); + return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText)); + } + } + } } } - /** * Adds an EmitHelper to a node. */ @@ -2930,7 +2937,7 @@ namespace ts { hasExportStarsToExportValues: boolean; // whether this module contains export* } - export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver): ExternalModuleInfo { + export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; const exportSpecifiers = createMap(); const exportedBindings = createMap(); @@ -2940,7 +2947,7 @@ namespace ts { let exportEquals: ExportAssignment = undefined; let hasExportStarsToExportValues = false; - const externalHelpersModuleName = getExternalHelpersModuleName(sourceFile); + const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(sourceFile, compilerOptions); const externalHelpersImportDeclaration = externalHelpersModuleName && createImportDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index ee9c081e6eb..1ae123ce5ca 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -26,84 +26,6 @@ namespace ts { EmitNotifications = 1 << 1, } - export interface TransformationResult { - /** - * Gets the transformed source files. - */ - transformed: SourceFile[]; - - /** - * Emits the substitute for a node, if one is available; otherwise, emits the node. - * - * @param emitContext The current emit context. - * @param node The node to substitute. - * @param emitCallback A callback used to emit the node or its substitute. - */ - emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; - - /** - * Emits a node with possible notification. - * - * @param emitContext The current emit context. - * @param node The node to emit. - * @param emitCallback A callback used to emit the node. - */ - emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; - } - - export interface TransformationContext extends LexicalEnvironment { - getCompilerOptions(): CompilerOptions; - getEmitResolver(): EmitResolver; - getEmitHost(): EmitHost; - - /** - * Hoists a function declaration to the containing scope. - */ - hoistFunctionDeclaration(node: FunctionDeclaration): void; - - /** - * Hoists a variable declaration to the containing scope. - */ - hoistVariableDeclaration(node: Identifier): void; - - /** - * Enables expression substitutions in the pretty printer for the provided SyntaxKind. - */ - enableSubstitution(kind: SyntaxKind): void; - - /** - * Determines whether expression substitutions are enabled for the provided node. - */ - isSubstitutionEnabled(node: Node): boolean; - - /** - * Hook used by transformers to substitute expressions just before they - * are emitted by the pretty printer. - */ - onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node; - - /** - * Enables before/after emit notifications in the pretty printer for the provided - * SyntaxKind. - */ - enableEmitNotification(kind: SyntaxKind): void; - - /** - * Determines whether before/after emit notifications should be raised in the pretty - * printer when it emits a node. - */ - isEmitNotificationEnabled(node: Node): boolean; - - /** - * Hook used to allow transformers to capture state before or after - * the printer emits a node. - */ - onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void; - } - - /* @internal */ - export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile; - export function getTransformers(compilerOptions: CompilerOptions) { const jsx = compilerOptions.jsx; const languageVersion = getEmitScriptTarget(compilerOptions); @@ -149,14 +71,18 @@ namespace ts { * @param transforms An array of Transformers. */ export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]): TransformationResult { - const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; - const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; const enabledSyntaxKindFeatures = new Array(SyntaxKind.Count); + let scopeModificationDisabled = false; + + let lexicalEnvironmentVariableDeclarations: VariableDeclaration[]; + let lexicalEnvironmentFunctionDeclarations: FunctionDeclaration[]; + let lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; + let lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; let lexicalEnvironmentStackOffset = 0; - let hoistedVariableDeclarations: VariableDeclaration[]; - let hoistedFunctionDeclarations: FunctionDeclaration[]; - let lexicalEnvironmentDisabled: boolean; + let lexicalEnvironmentSuspended = false; + + let emitHelpers: EmitHelper[]; // The transformation context is provided to each transformer as part of transformer // initialization. @@ -164,10 +90,14 @@ namespace ts { getCompilerOptions: () => host.getCompilerOptions(), getEmitResolver: () => resolver, getEmitHost: () => host, + startLexicalEnvironment, + suspendLexicalEnvironment, + resumeLexicalEnvironment, + endLexicalEnvironment, hoistVariableDeclaration, hoistFunctionDeclaration, - startLexicalEnvironment, - endLexicalEnvironment, + requestEmitHelper, + readEmitHelpers, onSubstituteNode: (_emitContext, node) => node, enableSubstitution, isSubstitutionEnabled, @@ -183,7 +113,7 @@ namespace ts { const transformed = map(sourceFiles, transformSourceFile); // Disable modification of the lexical environment. - lexicalEnvironmentDisabled = true; + scopeModificationDisabled = true; return { transformed, @@ -278,13 +208,13 @@ namespace ts { * Records a hoisted variable declaration for the provided name within a lexical environment. */ function hoistVariableDeclaration(name: Identifier): void { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase."); + Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); const decl = createVariableDeclaration(name); - if (!hoistedVariableDeclarations) { - hoistedVariableDeclarations = [decl]; + if (!lexicalEnvironmentVariableDeclarations) { + lexicalEnvironmentVariableDeclarations = [decl]; } else { - hoistedVariableDeclarations.push(decl); + lexicalEnvironmentVariableDeclarations.push(decl); } } @@ -292,31 +222,46 @@ namespace ts { * Records a hoisted function declaration within a lexical environment. */ function hoistFunctionDeclaration(func: FunctionDeclaration): void { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase."); - if (!hoistedFunctionDeclarations) { - hoistedFunctionDeclarations = [func]; + Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); + if (!lexicalEnvironmentFunctionDeclarations) { + lexicalEnvironmentFunctionDeclarations = [func]; } else { - hoistedFunctionDeclarations.push(func); + lexicalEnvironmentFunctionDeclarations.push(func); } } + /** Suspends the current lexical environment, usually after visiting a parameter list. */ + function suspendLexicalEnvironment(): void { + Debug.assert(!scopeModificationDisabled, "Cannot suspend a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is already suspended."); + lexicalEnvironmentSuspended = true; + } + + /** Resumes a suspended lexical environment, usually before visiting a function body. */ + function resumeLexicalEnvironment(): void { + Debug.assert(!scopeModificationDisabled, "Cannot resume a lexical environment during the print phase."); + Debug.assert(lexicalEnvironmentSuspended, "Lexical environment is not suspended suspended."); + lexicalEnvironmentSuspended = false; + } + /** * Starts a new lexical environment. Any existing hoisted variable or function declarations * are pushed onto a stack, and the related storage variables are reset. */ function startLexicalEnvironment(): void { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot start a lexical environment during the print phase."); + Debug.assert(!scopeModificationDisabled, "Cannot start a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended."); // Save the current lexical environment. Rather than resizing the array we adjust the // stack size variable. This allows us to reuse existing array slots we've // already allocated between transformations to avoid allocation and GC overhead during // transformation. - lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedVariableDeclarations; - lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedFunctionDeclarations; + lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentVariableDeclarations; + lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFunctionDeclarations; lexicalEnvironmentStackOffset++; - hoistedVariableDeclarations = undefined; - hoistedFunctionDeclarations = undefined; + lexicalEnvironmentVariableDeclarations = undefined; + lexicalEnvironmentFunctionDeclarations = undefined; } /** @@ -324,18 +269,19 @@ namespace ts { * any hoisted declarations added in this environment are returned. */ function endLexicalEnvironment(): Statement[] { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot end a lexical environment during the print phase."); + Debug.assert(!scopeModificationDisabled, "Cannot end a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended."); let statements: Statement[]; - if (hoistedVariableDeclarations || hoistedFunctionDeclarations) { - if (hoistedFunctionDeclarations) { - statements = [...hoistedFunctionDeclarations]; + if (lexicalEnvironmentVariableDeclarations || lexicalEnvironmentFunctionDeclarations) { + if (lexicalEnvironmentFunctionDeclarations) { + statements = [...lexicalEnvironmentFunctionDeclarations]; } - if (hoistedVariableDeclarations) { + if (lexicalEnvironmentVariableDeclarations) { const statement = createVariableStatement( /*modifiers*/ undefined, - createVariableDeclarationList(hoistedVariableDeclarations) + createVariableDeclarationList(lexicalEnvironmentVariableDeclarations) ); if (!statements) { @@ -349,9 +295,27 @@ namespace ts { // Restore the previous lexical environment. lexicalEnvironmentStackOffset--; - hoistedVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset]; - hoistedFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset]; + lexicalEnvironmentVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset]; + lexicalEnvironmentFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset]; + if (lexicalEnvironmentStackOffset === 0) { + lexicalEnvironmentVariableDeclarationsStack = []; + lexicalEnvironmentFunctionDeclarationsStack = []; + } + return statements; } + + function requestEmitHelper(helper: EmitHelper): void { + Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); + Debug.assert(!helper.scoped, "Cannot request a scoped emit helper."); + emitHelpers = append(emitHelpers, helper); + } + + function readEmitHelpers(): EmitHelper[] | undefined { + Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); + const helpers = emitHelpers; + emitHelpers = undefined; + return helpers; + } } } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 60d1ad95682..733e47eaa67 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -165,11 +165,11 @@ namespace ts { export function transformES2015(context: TransformationContext) { const { startLexicalEnvironment, + resumeLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, } = context; - const compilerOptions = context.getCompilerOptions(); const resolver = context.getEmitResolver(); const previousOnSubstituteNode = context.onSubstituteNode; const previousOnEmitNode = context.onEmitNode; @@ -187,7 +187,6 @@ namespace ts { let enclosingNonArrowFunction: FunctionLikeDeclaration; let enclosingNonAsyncFunctionBody: FunctionLikeDeclaration | ClassElement; let isInConstructorWithCapturedSuper: boolean; - let helperState: EmitHelperState; /** * Used to track if we are emitting body of the converted loop @@ -210,14 +209,12 @@ namespace ts { currentSourceFile = node; currentText = node.text; - helperState = { currentSourceFile, compilerOptions }; - const visited = visitNode(node, visitor, isSourceFile); - addEmitHelpers(visited, helperState.requestedHelpers); + const visited = saveStateAndInvoke(node, visitSourceFile); + addEmitHelpers(visited, context.readEmitHelpers()); currentSourceFile = undefined; currentText = undefined; - helperState = undefined; return visited; } @@ -264,6 +261,47 @@ namespace ts { return visited; } + function onBeforeVisitNode(node: Node) { + if (currentNode) { + if (isBlockScope(currentNode, currentParent)) { + enclosingBlockScopeContainer = currentNode; + enclosingBlockScopeContainerParent = currentParent; + } + + if (isFunctionLike(currentNode)) { + enclosingFunction = currentNode; + if (currentNode.kind !== SyntaxKind.ArrowFunction) { + enclosingNonArrowFunction = currentNode; + if (!(getEmitFlags(currentNode) & EmitFlags.AsyncFunctionBody)) { + enclosingNonAsyncFunctionBody = currentNode; + } + } + } + + // keep track of the enclosing variable statement when in the context of + // variable statements, variable declarations, binding elements, and binding + // patterns. + switch (currentNode.kind) { + case SyntaxKind.VariableStatement: + enclosingVariableStatement = currentNode; + break; + + case SyntaxKind.VariableDeclarationList: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BindingElement: + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + break; + + default: + enclosingVariableStatement = undefined; + } + } + + currentParent = currentNode; + currentNode = node; + } + function returnCapturedThis(node: Node): Node { return setOriginalNode(createReturn(createIdentifier("_this")), node); } @@ -288,7 +326,7 @@ namespace ts { else if (node.transformFlags & TransformFlags.ContainsES2015 || (isInConstructorWithCapturedSuper && !isExpression(node))) { // we want to dive in this branch either if node has children with ES2015 specific syntax // or we are inside constructor that captures result of the super call so all returns without expression should be - // rewritten. Note: we skip expressions since returns should never appear there + // rewritten. Note: we skip expressions since returns should never appear there return visitEachChild(node, visitor, context); } else { @@ -426,6 +464,9 @@ namespace ts { case SyntaxKind.YieldExpression: return visitYieldExpression(node); + case SyntaxKind.SpreadElementExpression: + return visitSpreadElementExpression(node); + case SyntaxKind.SuperKeyword: return visitSuperKeyword(); @@ -436,9 +477,6 @@ namespace ts { case SyntaxKind.MethodDeclaration: return visitMethodDeclaration(node); - case SyntaxKind.SourceFile: - return visitSourceFileNode(node); - case SyntaxKind.VariableStatement: return visitVariableStatement(node); @@ -446,48 +484,19 @@ namespace ts { Debug.failBadSyntaxKind(node); return visitEachChild(node, visitor, context); } - } - function onBeforeVisitNode(node: Node) { - if (currentNode) { - if (isBlockScope(currentNode, currentParent)) { - enclosingBlockScopeContainer = currentNode; - enclosingBlockScopeContainerParent = currentParent; - } - - if (isFunctionLike(currentNode)) { - enclosingFunction = currentNode; - if (currentNode.kind !== SyntaxKind.ArrowFunction) { - enclosingNonArrowFunction = currentNode; - if (!(getEmitFlags(currentNode) & EmitFlags.AsyncFunctionBody)) { - enclosingNonAsyncFunctionBody = currentNode; - } - } - } - - // keep track of the enclosing variable statement when in the context of - // variable statements, variable declarations, binding elements, and binding - // patterns. - switch (currentNode.kind) { - case SyntaxKind.VariableStatement: - enclosingVariableStatement = currentNode; - break; - - case SyntaxKind.VariableDeclarationList: - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ArrayBindingPattern: - break; - - default: - enclosingVariableStatement = undefined; - } - } - - currentParent = currentNode; - currentNode = node; + function visitSourceFile(node: SourceFile): SourceFile { + const statements: Statement[] = []; + startLexicalEnvironment(); + const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false, visitor); + addCaptureThisForNodeIfNeeded(statements, node); + addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); + addRange(statements, endLexicalEnvironment()); + return updateSourceFileNode( + node, + createNodeArray(statements, node.statements) + ); } function visitSwitchStatement(node: SwitchStatement): SwitchStatement { @@ -787,7 +796,7 @@ namespace ts { if (extendsClauseElement) { statements.push( createStatement( - createExtendsHelper(helperState, getLocalName(node)), + createExtendsHelper(context, getLocalName(node)), /*location*/ extendsClauseElement ) ); @@ -837,11 +846,8 @@ namespace ts { // `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); - } - - return []; + return visitParameterList(constructor && !hasSynthesizedSuper && constructor.parameters, visitor, context) + || []; } /** @@ -855,14 +861,14 @@ namespace ts { */ function transformConstructorBody(constructor: ConstructorDeclaration | undefined, node: ClassDeclaration | ClassExpression, extendsClauseElement: ExpressionWithTypeArguments, hasSynthesizedSuper: boolean) { const statements: Statement[] = []; - startLexicalEnvironment(); + resumeLexicalEnvironment(); let statementOffset = -1; if (hasSynthesizedSuper) { // If a super call has already been synthesized, // we're going to assume that we should just transform everything after that. // The assumption is that no prior step in the pipeline has added any prologue directives. - statementOffset = 1; + statementOffset = 0; } else if (constructor) { // Otherwise, try to emit all potential prologue directives first. @@ -1386,20 +1392,13 @@ namespace ts { function transformClassMethodDeclarationToStatement(receiver: LeftHandSideExpression, member: MethodDeclaration) { const commentRange = getCommentRange(member); const sourceMapRange = getSourceMapRange(member); - - const func = transformFunctionLikeToExpression(member, /*location*/ member, /*name*/ undefined); - setEmitFlags(func, EmitFlags.NoComments); - setSourceMapRange(func, sourceMapRange); + const memberName = createMemberAccessForPropertyName(receiver, visitNode(member.name, visitor, isPropertyName), /*location*/ member.name); + const memberFunction = transformFunctionLikeToExpression(member, /*location*/ member, /*name*/ undefined); + setEmitFlags(memberFunction, EmitFlags.NoComments); + setSourceMapRange(memberFunction, sourceMapRange); const statement = createStatement( - createAssignment( - createMemberAccessForPropertyName( - receiver, - visitNode(member.name, visitor, isPropertyName), - /*location*/ member.name - ), - func - ), + createAssignment(memberName, memberFunction), /*location*/ member ); @@ -1497,8 +1496,17 @@ namespace ts { if (node.transformFlags & TransformFlags.ContainsLexicalThis) { enableSubstitutionsForCapturedThis(); } - - const func = transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined); + const func = createFunctionExpression( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + /*name*/ undefined, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + transformFunctionBody(node), + node + ); + setOriginalNode(func, node); setEmitFlags(func, EmitFlags.CapturesThis); return func; } @@ -1509,7 +1517,17 @@ namespace ts { * @param node a FunctionExpression node. */ function visitFunctionExpression(node: FunctionExpression): Expression { - return transformFunctionLikeToExpression(node, /*location*/ node, node.name); + return updateFunctionExpression( + node, + /*modifiers*/ undefined, + node.name, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + node.transformFlags & TransformFlags.ES2015 + ? transformFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) + ); } /** @@ -1518,19 +1536,18 @@ namespace ts { * @param node a FunctionDeclaration node. */ function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { - return setOriginalNode( - createFunctionDeclaration( - /*decorators*/ undefined, - node.modifiers, - node.asteriskToken, - node.name, - /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), - /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node - ), - /*original*/ node); + return updateFunctionDeclaration( + node, + /*decorators*/ undefined, + node.modifiers, + node.name, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + node.transformFlags & TransformFlags.ES2015 + ? transformFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) + ); } /** @@ -1552,7 +1569,7 @@ namespace ts { node.asteriskToken, name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, saveStateAndInvoke(node, transformFunctionBody), location @@ -1579,7 +1596,8 @@ namespace ts { const body = node.body; let statementOffset: number; - startLexicalEnvironment(); + resumeLexicalEnvironment(); + if (isBlock(body)) { // ensureUseStrict is false because no new prologue-directive should be added. // addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array @@ -1633,15 +1651,21 @@ namespace ts { closeBraceLocation = body; } - const lexicalEnvironment = endLexicalEnvironment(); - addRange(statements, lexicalEnvironment); + const declarations = endLexicalEnvironment(); + addRange(statements, declarations); // If we added any final generated statements, this must be a multi-line block - if (!multiLine && lexicalEnvironment && lexicalEnvironment.length) { + if (!multiLine && declarations && declarations.length) { multiLine = true; } - const block = createBlock(createNodeArray(statements, statementsLocation), node.body, multiLine); + const block = createBlock( + createNodeArray(statements, statementsLocation), + node.body, + multiLine); + + setOriginalNode(block, node.body); + if (!multiLine && singleLine) { setEmitFlags(block, EmitFlags.SingleLine); } @@ -1650,7 +1674,6 @@ namespace ts { setTokenSourceMapRange(block, SyntaxKind.CloseBraceToken, closeBraceLocation); } - setOriginalNode(block, node.body); return block; } @@ -1659,7 +1682,7 @@ namespace ts { * * @param node An ExpressionStatement node. */ - function visitExpressionStatement(node: ExpressionStatement): ExpressionStatement { + function visitExpressionStatement(node: ExpressionStatement): Statement { // If we are here it is most likely because our expression is a destructuring assignment. switch (node.expression.kind) { case SyntaxKind.ParenthesizedExpression: @@ -1713,8 +1736,11 @@ namespace ts { */ function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { // If we are here it is because this is a destructuring assignment. - Debug.assert(isDestructuringAssignment(node)); - return flattenDestructuringAssignment(context, node, needsDestructuringValue, hoistVariableDeclaration, visitor); + if (isDestructuringAssignment(node)) { + return flattenDestructuringAssignment(context, node, needsDestructuringValue, hoistVariableDeclaration, visitor); + } + + return visitEachChild(node, visitor, context); } function visitVariableStatement(node: VariableStatement): Statement { @@ -2894,6 +2920,10 @@ namespace ts { ); } + function visitSpreadElementExpression(node: SpreadElementExpression) { + return visitNode(node.expression, visitor, isExpression); + } + /** * Transforms the expression of a SpreadElementExpression node. * @@ -3080,19 +3110,6 @@ namespace ts { : createIdentifier("_super"); } - function visitSourceFileNode(node: SourceFile): SourceFile { - const [prologue, remaining] = span(node.statements, isPrologueDirective); - const statements: Statement[] = []; - startLexicalEnvironment(); - addRange(statements, prologue); - addCaptureThisForNodeIfNeeded(statements, node); - addRange(statements, visitNodes(createNodeArray(remaining), visitor, isStatement)); - addRange(statements, endLexicalEnvironment()); - const clone = getMutableClone(node); - clone.statements = createNodeArray(statements, /*location*/ node.statements); - return clone; - } - /** * Called by the printer just before a node is printed. * @@ -3254,8 +3271,7 @@ namespace ts { return false; } - const parameter = singleOrUndefined(constructor.parameters); - if (!parameter || !nodeIsSynthesized(parameter) || !parameter.dotDotDotToken) { + if (some(constructor.parameters)) { return false; } @@ -3280,14 +3296,14 @@ namespace ts { } const expression = (callArgument).expression; - return isIdentifier(expression) && expression === parameter.name; + return isIdentifier(expression) && expression.text === "arguments"; } } - function createExtendsHelper(helperState: EmitHelperState, name: Identifier) { - requestEmitHelper(helperState, extendsHelper); + function createExtendsHelper(context: TransformationContext, name: Identifier) { + context.requestEmitHelper(extendsHelper); return createCall( - getHelperName(helperState, "__extends"), + getHelperName("__extends"), /*typeArguments*/ undefined, [ name, diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 14d97ade2ac..eca0e9a1488 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -13,6 +13,7 @@ namespace ts { export function transformES2017(context: TransformationContext) { const { startLexicalEnvironment, + resumeLexicalEnvironment, endLexicalEnvironment, } = context; @@ -22,7 +23,6 @@ namespace ts { // These variables contain state that changes as we descend into the tree. let currentSourceFile: SourceFile; - let helperState: EmitHelperState; /** * Keeps track of whether expression substitution has been enabled for specific edge cases. @@ -50,8 +50,6 @@ namespace ts { context.onEmitNode = onEmitNode; context.onSubstituteNode = onSubstituteNode; - let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; - return transformSourceFile; function transformSourceFile(node: SourceFile) { @@ -60,14 +58,11 @@ namespace ts { } currentSourceFile = node; - helperState = { currentSourceFile, compilerOptions }; const visited = visitEachChild(node, visitor, context); - - addEmitHelpers(visited, helperState.requestedHelpers); + addEmitHelpers(visited, context.readEmitHelpers()); currentSourceFile = undefined; - helperState = undefined; return visited; } @@ -142,22 +137,17 @@ namespace ts { */ function visitMethodDeclaration(node: MethodDeclaration) { Debug.assert(hasModifier(node, ModifierFlags.Async)); - const method = createMethod( + const updated = updateMethod( + node, /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + transformFunctionBody(node) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setOriginalNode(method, node); - return method; + return updated; } /** @@ -170,20 +160,17 @@ namespace ts { */ function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { Debug.assert(hasModifier(node, ModifierFlags.Async)); - const func = createFunctionDeclaration( + const updated = updateFunctionDeclaration( + node, /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + transformFunctionBody(node) ); - - setOriginalNode(func, node); - return func; + return updated; } /** @@ -199,20 +186,18 @@ namespace ts { if (nodeIsMissing(node.body)) { return createOmittedExpression(); } - - const func = createFunctionExpression( - /*modifiers*/ undefined, - node.asteriskToken, + const updated = updateFunctionExpression( + node, + visitNodes(node.modifiers, visitor, isModifier), node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + transformFunctionBody(node) ); - setOriginalNode(func, node); - return func; + setOriginalNode(updated, node); + return updated; } /** @@ -225,42 +210,24 @@ namespace ts { */ function visitArrowFunction(node: ArrowFunction) { Debug.assert(hasModifier(node, ModifierFlags.Async)); - const func = createArrowFunction( + const updated = updateArrowFunction( + node, visitNodes(node.modifiers, visitor, isModifier), /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - node.equalsGreaterThanToken, - transformConciseBody(node), - /*location*/ node + transformFunctionBody(node) ); - setOriginalNode(func, node); - return func; + setOriginalNode(updated, node); + return updated; } - function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody { - return transformAsyncFunctionBody(node); - } - - function transformConciseBody(node: ArrowFunction): ConciseBody { - return transformAsyncFunctionBody(node); - } - - function transformFunctionBodyWorker(body: Block, start = 0) { - const savedCurrentScope = currentScope; - currentScope = body; - startLexicalEnvironment(); - - const statements = visitNodes(body.statements, visitor, isStatement, start); - const visited = updateBlock(body, statements); - const declarations = endLexicalEnvironment(); - currentScope = savedCurrentScope; - return mergeFunctionBodyLexicalEnvironment(visited, declarations); - } - - function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody | FunctionBody { - const nodeType = node.original ? (node.original).type : node.type; + function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody; + function transformFunctionBody(node: ArrowFunction): ConciseBody; + function transformFunctionBody(node: FunctionLikeDeclaration): ConciseBody { + const original = getOriginalNode(node, isFunctionLike); + const nodeType = original.type; const promiseConstructor = languageVersion < ScriptTarget.ES2015 ? getPromiseConstructor(nodeType) : undefined; const isArrowFunction = node.kind === SyntaxKind.ArrowFunction; const hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0; @@ -271,6 +238,7 @@ namespace ts { // passed to `__awaiter` is executed inside of the callback to the // promise constructor. + resumeLexicalEnvironment(); if (!isArrowFunction) { const statements: Statement[] = []; @@ -278,7 +246,7 @@ namespace ts { statements.push( createReturn( createAwaiterHelper( - helperState, + context, hasLexicalArguments, promiseConstructor, transformFunctionBodyWorker(node.body, statementOffset) @@ -286,6 +254,8 @@ namespace ts { ) ); + addRange(statements, endLexicalEnvironment()); + const block = createBlock(statements, /*location*/ node.body, /*multiLine*/ true); // Minor optimization, emit `_super` helper to capture `super` access in an arrow. @@ -304,37 +274,40 @@ namespace ts { return block; } else { - return createAwaiterHelper( - helperState, + const expression = createAwaiterHelper( + context, hasLexicalArguments, promiseConstructor, - transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ true) + transformFunctionBodyWorker(node.body) ); + + const declarations = endLexicalEnvironment(); + if (some(declarations)) { + const block = convertToFunctionBody(expression); + const statements = mergeLexicalEnvironment(block.statements, declarations); + return updateBlock(block, statements); + } + + return expression; } } - function transformConciseBodyWorker(body: Block | Expression, forceBlockFunctionBody: boolean) { + function transformFunctionBodyWorker(body: ConciseBody, start?: number) { if (isBlock(body)) { - return transformFunctionBodyWorker(body); + return updateBlock( + body, + visitLexicalEnvironment(body.statements, visitor, context, start)); } else { startLexicalEnvironment(); - const visited: Expression | Block = visitNode(body, visitor, isConciseBody); - const declarations = endLexicalEnvironment(); - const merged = mergeFunctionBodyLexicalEnvironment(visited, declarations); - if (forceBlockFunctionBody && !isBlock(merged)) { - return createBlock([ - createReturn(merged) - ]); - } - else { - return merged; - } + const visited = convertToFunctionBody(visitNode(body, visitor, isConciseBody)); + const statements = mergeLexicalEnvironment(visited.statements, endLexicalEnvironment()); + return updateBlock(visited, statements); } } function getPromiseConstructor(type: TypeNode) { - const typeName = getEntityNameFromTypeNode(type); + const typeName = type && getEntityNameFromTypeNode(type); if (typeName && isEntityName(typeName)) { const serializationKind = resolver.getTypeReferenceSerializationKind(typeName); if (serializationKind === TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue @@ -506,7 +479,8 @@ namespace ts { } } - function createAwaiterHelper(helperState: EmitHelperState, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { + function createAwaiterHelper(context: TransformationContext, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { + context.requestEmitHelper(awaiterHelper); const generatorFunc = createFunctionExpression( /*modifiers*/ undefined, createToken(SyntaxKind.AsteriskToken), @@ -520,9 +494,8 @@ namespace ts { // Mark this node as originally an async function (generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody; - requestEmitHelper(helperState, awaiterHelper); return createCall( - getHelperName(helperState, "__awaiter"), + getHelperName("__awaiter"), /*typeArguments*/ undefined, [ createThis(), diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 78c9e42c1bf..6047a8d8edd 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -242,7 +242,6 @@ namespace ts { let currentSourceFile: SourceFile; let renamedCatchVariables: Map; let renamedCatchVariableDeclarations: Map; - let helperState: EmitHelperState; let inGeneratorFunctionBody: boolean; let inStatementContainingYield: boolean; @@ -298,13 +297,11 @@ namespace ts { } currentSourceFile = node; - helperState = { currentSourceFile, compilerOptions }; const visited = visitEachChild(node, visitor, context); - addEmitHelpers(visited, helperState.requestedHelpers); + addEmitHelpers(visited, context.readEmitHelpers()); currentSourceFile = undefined; - helperState = undefined; return visited; } @@ -2590,7 +2587,7 @@ namespace ts { const buildResult = buildStatements(); return createGeneratorHelper( - helperState, + context, setEmitFlags( createFunctionExpression( /*modifiers*/ undefined, @@ -3083,10 +3080,10 @@ namespace ts { } } - function createGeneratorHelper(helperState: EmitHelperState, body: FunctionExpression) { - requestEmitHelper(helperState, generatorHelper); + function createGeneratorHelper(context: TransformationContext, body: FunctionExpression) { + context.requestEmitHelper(generatorHelper); return createCall( - getHelperName(helperState, "__generator"), + getHelperName("__generator"), /*typeArguments*/ undefined, [createThis(), body]); } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 8867896e51e..e342f2b7906 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -6,7 +6,6 @@ namespace ts { export function transformJsx(context: TransformationContext) { const compilerOptions = context.getCompilerOptions(); let currentSourceFile: SourceFile; - let helperState: EmitHelperState; return transformSourceFile; @@ -21,13 +20,11 @@ namespace ts { } currentSourceFile = node; - helperState = { currentSourceFile, compilerOptions }; const visited = visitEachChild(node, visitor, context); - addEmitHelpers(visited, helperState.requestedHelpers); + addEmitHelpers(visited, context.readEmitHelpers()); currentSourceFile = undefined; - helperState = undefined; return visited; } @@ -116,7 +113,7 @@ namespace ts { // a call to the __assign helper. objectProperties = singleOrUndefined(segments); if (!objectProperties) { - objectProperties = createAssignHelper(helperState, segments); + objectProperties = createAssignHelper(context, segments); } } @@ -538,10 +535,10 @@ namespace ts { "diams": 0x2666 }); - function createAssignHelper(helperState: EmitHelperState, attributesSegments: Expression[]) { - requestEmitHelper(helperState, assignHelper); + function createAssignHelper(context: TransformationContext, attributesSegments: Expression[]) { + context.requestEmitHelper(assignHelper); return createCall( - getHelperName(helperState, "__assign"), + getHelperName("__assign"), /*typeArguments*/ undefined, attributesSegments ); diff --git a/src/compiler/transformers/module/es2015.ts b/src/compiler/transformers/module/es2015.ts index daef9cb5a4d..5611f890164 100644 --- a/src/compiler/transformers/module/es2015.ts +++ b/src/compiler/transformers/module/es2015.ts @@ -5,6 +5,14 @@ namespace ts { export function transformES2015Module(context: TransformationContext) { const compilerOptions = context.getCompilerOptions(); + const previousOnEmitNode = context.onEmitNode; + const previousOnSubstituteNode = context.onSubstituteNode; + context.onEmitNode = onEmitNode; + context.onSubstituteNode = onSubstituteNode; + context.enableEmitNotification(SyntaxKind.SourceFile); + context.enableSubstitution(SyntaxKind.Identifier); + + let currentSourceFile: SourceFile; return transformSourceFile; function transformSourceFile(node: SourceFile) { @@ -13,7 +21,7 @@ namespace ts { } if (isExternalModule(node) || compilerOptions.isolatedModules) { - const externalHelpersModuleName = getExternalHelpersModuleName(node); + const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(node, compilerOptions); if (externalHelpersModuleName) { const statements: Statement[] = []; const statementOffset = addPrologueDirectives(statements, node.statements); @@ -55,5 +63,55 @@ namespace ts { // Elide `export=` as it is not legal with --module ES6 return node.isExportEquals ? undefined : node; } + + // + // Emit Notification + // + + /** + * Hook for node emit. + * + * @param emitContext A context hint for the emitter. + * @param node The node to emit. + * @param emit A callback used to emit the node in the printer. + */ + function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { + if (isSourceFile(node)) { + currentSourceFile = node; + previousOnEmitNode(emitContext, node, emitCallback); + currentSourceFile = undefined; + } + else { + previousOnEmitNode(emitContext, node, emitCallback); + } + } + + // + // Substitutions + // + + /** + * Hooks node substitutions. + * + * @param emitContext A context hint for the emitter. + * @param node The node to substitute. + */ + function onSubstituteNode(emitContext: EmitContext, node: Node) { + node = previousOnSubstituteNode(emitContext, node); + if (isIdentifier(node) && emitContext === EmitContext.Expression) { + return substituteExpressionIdentifier(node); + } + return node; + } + + function substituteExpressionIdentifier(node: Identifier): Expression { + if (getEmitFlags(node) & EmitFlags.HelperName) { + const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile); + if (externalHelpersModuleName) { + return createPropertyAccess(externalHelpersModuleName, node); + } + } + return node; + } } } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 22e4e844a69..7b6fcc60f1b 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -61,7 +61,7 @@ namespace ts { } currentSourceFile = node; - currentModuleInfo = moduleInfoMap[getOriginalNodeId(node)] = collectExternalModuleInfo(node, resolver); + currentModuleInfo = moduleInfoMap[getOriginalNodeId(node)] = collectExternalModuleInfo(node, resolver, compilerOptions); // Perform the transformation. const transformModule = transformModuleDelegates[moduleKind] || transformModuleDelegates[ModuleKind.None]; @@ -1187,6 +1187,14 @@ namespace ts { * @param node The node to substitute. */ function substituteExpressionIdentifier(node: Identifier): Expression { + if (getEmitFlags(node) & EmitFlags.HelperName) { + const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile); + if (externalHelpersModuleName) { + return createPropertyAccess(externalHelpersModuleName, node); + } + return node; + } + if (!isGeneratedIdentifier(node) && !isLocalName(node)) { const exportContainer = resolver.getReferencedExportContainer(node, isExportName(node)); if (exportContainer && exportContainer.kind === SyntaxKind.SourceFile) { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 4b24d5c1641..b979f66efd1 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -73,7 +73,7 @@ namespace ts { // see comment to 'substitutePostfixUnaryExpression' for more details // Collect information about the external module and dependency groups. - moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(node, resolver); + moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(node, resolver, compilerOptions); // Make sure that the name of the 'exports' function does not conflict with // existing identifiers. @@ -1609,6 +1609,14 @@ namespace ts { * @param node The node to substitute. */ function substituteExpressionIdentifier(node: Identifier): Expression { + if (getEmitFlags(node) & EmitFlags.HelperName) { + const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile); + if (externalHelpersModuleName) { + return createPropertyAccess(externalHelpersModuleName, node); + } + return node; + } + // When we see an identifier in an expression position that // points to an imported symbol, we should substitute a qualified // reference to the imported symbol if one is needed. diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 02c7b107b25..09bfba2263e 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -21,6 +21,7 @@ namespace ts { export function transformTypeScript(context: TransformationContext) { const { startLexicalEnvironment, + resumeLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, } = context; @@ -48,7 +49,6 @@ namespace ts { let currentNamespaceContainerName: Identifier; let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; let currentScopeFirstDeclarationsOfName: Map; - let helperState: EmitHelperState; /** * Keeps track of whether expression substitution has been enabled for specific edge cases. @@ -81,21 +81,11 @@ namespace ts { } currentSourceFile = node; - currentScope = node; - currentScopeFirstDeclarationsOfName = createMap(); - helperState = { currentSourceFile, compilerOptions }; - let visited = visitEachChild(node, sourceElementVisitor, context); - if (compilerOptions.alwaysStrict) { - visited = updateSourceFileNode(visited, ensureUseStrict(visited.statements)); - } - - addEmitHelpers(visited, helperState.requestedHelpers); + const visited = saveStateAndInvoke(node, visitSourceFile); + addEmitHelpers(visited, context.readEmitHelpers()); currentSourceFile = undefined; - currentScope = undefined; - currentScopeFirstDeclarationsOfName = undefined; - helperState = undefined; return visited; } @@ -123,6 +113,32 @@ namespace ts { return visited; } + /** + * Performs actions that should always occur immediately before visiting a node. + * + * @param node The node to visit. + */ + function onBeforeVisitNode(node: Node) { + switch (node.kind) { + case SyntaxKind.SourceFile: + case SyntaxKind.CaseBlock: + case SyntaxKind.ModuleBlock: + case SyntaxKind.Block: + currentScope = node; + currentScopeFirstDeclarationsOfName = undefined; + break; + + case SyntaxKind.ClassDeclaration: + case SyntaxKind.FunctionDeclaration: + if (hasModifier(node, ModifierFlags.Ambient)) { + break; + } + + recordEmittedDeclarationInScope(node); + break; + } + } + /** * General-purpose node visitor. * @@ -451,29 +467,10 @@ namespace ts { } } - /** - * Performs actions that should always occur immediately before visiting a node. - * - * @param node The node to visit. - */ - function onBeforeVisitNode(node: Node) { - switch (node.kind) { - case SyntaxKind.CaseBlock: - case SyntaxKind.ModuleBlock: - case SyntaxKind.Block: - currentScope = node; - currentScopeFirstDeclarationsOfName = undefined; - break; - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.FunctionDeclaration: - if (hasModifier(node, ModifierFlags.Ambient)) { - break; - } - - recordEmittedDeclarationInScope(node); - break; - } + function visitSourceFile(node: SourceFile) { + return updateSourceFileNode( + node, + visitLexicalEnvironment(node.statements, sourceElementVisitor, context, /*start*/ 0, compilerOptions.alwaysStrict)); } /** @@ -845,9 +842,8 @@ namespace ts { // downlevel the '...args' portion less efficiently by naively copying the contents of 'arguments' to an array. // Instead, we'll avoid using a rest parameter and spread into the super call as // 'super(...arguments)' instead of 'super(...args)', as you can see in "transformConstructorBody". - return constructor - ? visitNodes(constructor.parameters, visitor, isParameter) - : []; + return visitParameterList(constructor && constructor.parameters, visitor, context) + || []; } /** @@ -863,7 +859,7 @@ namespace ts { let indexOfFirstStatement = 0; // The body of a constructor is a new lexical environment - startLexicalEnvironment(); + resumeLexicalEnvironment(); if (constructor) { indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements); @@ -919,15 +915,13 @@ namespace ts { // End the lexical environment. addRange(statements, endLexicalEnvironment()); - return setMultiLine( - createBlock( - createNodeArray( - statements, - /*location*/ constructor ? constructor.body.statements : node.members - ), - /*location*/ constructor ? constructor.body : /*location*/ undefined + return createBlock( + createNodeArray( + statements, + /*location*/ constructor ? constructor.body.statements : node.members ), - true + /*location*/ constructor ? constructor.body : /*location*/ undefined, + /*multiLine*/ true ); } @@ -1385,7 +1379,7 @@ namespace ts { : undefined; const helper = createDecorateHelper( - helperState, + context, decoratorExpressions, prefix, memberName, @@ -1423,7 +1417,7 @@ namespace ts { const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; const localName = getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); - const decorate = createDecorateHelper(helperState, decoratorExpressions, localName); + const decorate = createDecorateHelper(context, decoratorExpressions, localName); const expression = createAssignment(localName, classAlias ? createAssignment(classAlias, decorate) : decorate); setEmitFlags(expression, EmitFlags.NoComments); setSourceMapRange(expression, moveRangePastDecorators(node)); @@ -1451,7 +1445,7 @@ namespace ts { expressions = []; for (const decorator of decorators) { const helper = createParamHelper( - helperState, + context, transformDecorator(decorator), parameterOffset, /*location*/ decorator.expression); @@ -1481,13 +1475,13 @@ namespace ts { function addOldTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) { if (compilerOptions.emitDecoratorMetadata) { if (shouldAddTypeMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(helperState, "design:type", serializeTypeOfNode(node))); + decoratorExpressions.push(createMetadataHelper(context, "design:type", serializeTypeOfNode(node))); } if (shouldAddParamTypesMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(helperState, "design:paramtypes", serializeParameterTypesOfNode(node))); + decoratorExpressions.push(createMetadataHelper(context, "design:paramtypes", serializeParameterTypesOfNode(node))); } if (shouldAddReturnTypeMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(helperState, "design:returntype", serializeReturnTypeOfNode(node))); + decoratorExpressions.push(createMetadataHelper(context, "design:returntype", serializeReturnTypeOfNode(node))); } } } @@ -1505,7 +1499,7 @@ namespace ts { (properties || (properties = [])).push(createPropertyAssignment("returnType", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), serializeReturnTypeOfNode(node)))); } if (properties) { - decoratorExpressions.push(createMetadataHelper(helperState, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true))); + decoratorExpressions.push(createMetadataHelper(context, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true))); } } } @@ -2020,26 +2014,23 @@ namespace ts { if (!shouldEmitFunctionLikeDeclaration(node)) { return undefined; } - - const method = createMethod( + const updated = updateMethod( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), - node.asteriskToken, visitPropertyNameOfClassElement(node), /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setCommentRange(method, node); - setSourceMapRange(method, moveRangePastDecorators(node)); - setOriginalNode(method, node); - - return method; + if (updated !== node) { + // While we emit the source map for the node after skipping decorators and modifiers, + // we need to emit the comments for the original range. + setCommentRange(updated, node); + setSourceMapRange(updated, moveRangePastDecorators(node)); + } + return updated; } /** @@ -2065,24 +2056,22 @@ namespace ts { if (!shouldEmitAccessorDeclaration(node)) { return undefined; } - - const accessor = createGetAccessor( + const updated = updateGetAccessor( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), visitPropertyNameOfClassElement(node), - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - node.body ? visitEachChild(node.body, visitor, context) : createBlock([]), - /*location*/ node + visitFunctionBody(node.body, visitor, context) || createBlock([]) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setOriginalNode(accessor, node); - setCommentRange(accessor, node); - setSourceMapRange(accessor, moveRangePastDecorators(node)); - - return accessor; + if (updated !== node) { + // While we emit the source map for the node after skipping decorators and modifiers, + // we need to emit the comments for the original range. + setCommentRange(updated, node); + setSourceMapRange(updated, moveRangePastDecorators(node)); + } + return updated; } /** @@ -2098,23 +2087,21 @@ namespace ts { if (!shouldEmitAccessorDeclaration(node)) { return undefined; } - - const accessor = createSetAccessor( + const updated = updateSetAccessor( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), visitPropertyNameOfClassElement(node), - visitNodes(node.parameters, visitor, isParameter), - node.body ? visitEachChild(node.body, visitor, context) : createBlock([]), - /*location*/ node + visitParameterList(node.parameters, visitor, context), + visitFunctionBody(node.body, visitor, context) || createBlock([]) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setOriginalNode(accessor, node); - setCommentRange(accessor, node); - setSourceMapRange(accessor, moveRangePastDecorators(node)); - - return accessor; + if (updated !== node) { + // While we emit the source map for the node after skipping decorators and modifiers, + // we need to emit the comments for the original range. + setCommentRange(updated, node); + setSourceMapRange(updated, moveRangePastDecorators(node)); + } + return updated; } /** @@ -2131,27 +2118,22 @@ namespace ts { if (!shouldEmitFunctionLikeDeclaration(node)) { return createNotEmittedStatement(node); } - - const func = createFunctionDeclaration( + const updated = updateFunctionDeclaration( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) || createBlock([]) ); - setOriginalNode(func, node); - if (isNamespaceExport(node)) { - const statements: Statement[] = [func]; + const statements: Statement[] = [updated]; addExportMemberAssignment(statements, node); return statements; } - - return func; + return updated; } /** @@ -2166,21 +2148,16 @@ namespace ts { if (nodeIsMissing(node.body)) { return createOmittedExpression(); } - - const func = createFunctionExpression( + const updated = updateFunctionExpression( + node, visitNodes(node.modifiers, modifierVisitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) ); - - setOriginalNode(func, node); - - return func; + return updated; } /** @@ -2189,62 +2166,15 @@ namespace ts { * - The node has type annotations */ function visitArrowFunction(node: ArrowFunction) { - const func = createArrowFunction( + const updated = updateArrowFunction( + node, visitNodes(node.modifiers, modifierVisitor, isModifier), /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - node.equalsGreaterThanToken, - transformConciseBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) ); - - setOriginalNode(func, node); - - return func; - } - - function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody { - return transformFunctionBodyWorker(node.body); - } - - function transformFunctionBodyWorker(body: Block, start = 0) { - const savedCurrentScope = currentScope; - const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName; - currentScope = body; - currentScopeFirstDeclarationsOfName = createMap(); - startLexicalEnvironment(); - - const statements = visitNodes(body.statements, visitor, isStatement, start); - const visited = updateBlock(body, statements); - const declarations = endLexicalEnvironment(); - currentScope = savedCurrentScope; - currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName; - return mergeFunctionBodyLexicalEnvironment(visited, declarations); - } - - function transformConciseBody(node: ArrowFunction): ConciseBody { - return transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ false); - } - - function transformConciseBodyWorker(body: Block | Expression, forceBlockFunctionBody: boolean) { - if (isBlock(body)) { - return transformFunctionBodyWorker(body); - } - else { - startLexicalEnvironment(); - const visited: Expression | Block = visitNode(body, visitor, isConciseBody); - const declarations = endLexicalEnvironment(); - const merged = mergeFunctionBodyLexicalEnvironment(visited, declarations); - if (forceBlockFunctionBody && !isBlock(merged)) { - return createBlock([ - createReturn(merged) - ]); - } - else { - return merged; - } - } + return updated; } /** @@ -3316,7 +3246,7 @@ namespace ts { function trySubstituteNamespaceExportedName(node: Identifier): Expression { // If this is explicitly a local name, do not substitute. - if (enabledSubstitutions & applicableSubstitutions && !isLocalName(node)) { + if (enabledSubstitutions & applicableSubstitutions && !isGeneratedIdentifier(node) && !isLocalName(node)) { // If we are nested within a namespace declaration, we may need to qualifiy // an identifier that is exported from a merged namespace. const container = resolver.getReferencedExportContainer(node, /*prefixLocals*/ false); @@ -3372,10 +3302,20 @@ namespace ts { } } - function createParamHelper(helperState: EmitHelperState, expression: Expression, parameterOffset: number, location?: TextRange) { - requestEmitHelper(helperState, paramHelper); + const paramHelper: EmitHelper = { + name: "typescript:param", + scoped: false, + priority: 4, + text: ` + var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } + };` + }; + + function createParamHelper(context: TransformationContext, expression: Expression, parameterOffset: number, location?: TextRange) { + context.requestEmitHelper(paramHelper); return createCall( - getHelperName(helperState, "__param"), + getHelperName("__param"), /*typeArguments*/ undefined, [ createLiteral(parameterOffset), @@ -3385,10 +3325,20 @@ namespace ts { ); } - function createMetadataHelper(helperState: EmitHelperState, metadataKey: string, metadataValue: Expression) { - requestEmitHelper(helperState, metadataHelper); + const metadataHelper: EmitHelper = { + name: "typescript:metadata", + scoped: false, + priority: 3, + text: ` + var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + };` + }; + + function createMetadataHelper(context: TransformationContext, metadataKey: string, metadataValue: Expression) { + context.requestEmitHelper(metadataHelper); return createCall( - getHelperName(helperState, "__metadata"), + getHelperName("__metadata"), /*typeArguments*/ undefined, [ createLiteral(metadataKey), @@ -3397,20 +3347,6 @@ namespace ts { ); } - function createDecorateHelper(helperState: EmitHelperState, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) { - const argumentsArray: Expression[] = []; - argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true)); - argumentsArray.push(target); - if (memberName) { - argumentsArray.push(memberName); - if (descriptor) { - argumentsArray.push(descriptor); - } - } - - requestEmitHelper(helperState, decorateHelper); - return createCall(getHelperName(helperState, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location); - } const decorateHelper: EmitHelper = { name: "typescript:decorate", @@ -3425,23 +3361,18 @@ namespace ts { };` }; - const metadataHelper: EmitHelper = { - name: "typescript:metadata", - scoped: false, - priority: 3, - text: ` - var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - };` - }; + function createDecorateHelper(context: TransformationContext, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) { + context.requestEmitHelper(decorateHelper); + const argumentsArray: Expression[] = []; + argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true)); + argumentsArray.push(target); + if (memberName) { + argumentsArray.push(memberName); + if (descriptor) { + argumentsArray.push(descriptor); + } + } - const paramHelper: EmitHelper = { - name: "typescript:param", - scoped: false, - priority: 4, - text: ` - var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } - };` - }; + return createCall(getHelperName("__decorate"), /*typeArguments*/ undefined, argumentsArray, location); + } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 69ac1d76c9e..178e0bf6de7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1503,6 +1503,11 @@ namespace ts { expression: Expression; } + /* @internal */ + export interface PrologueDirective extends ExpressionStatement { + expression: StringLiteral; + } + export interface IfStatement extends Statement { kind: SyntaxKind.IfStatement; expression: Expression; @@ -3541,15 +3546,16 @@ namespace ts { NoTrailingComments = 1 << 10, // Do not emit trailing comments for this node. NoComments = NoLeadingComments | NoTrailingComments, // Do not emit comments for this node. NoNestedComments = 1 << 11, - ExportName = 1 << 12, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). - LocalName = 1 << 13, // Ensure an export prefix is not added for an identifier that points to an exported declaration. - Indented = 1 << 14, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). - NoIndentation = 1 << 15, // Do not indent the node. - AsyncFunctionBody = 1 << 16, - ReuseTempVariableScope = 1 << 17, // Reuse the existing temp variable scope during emit. - CustomPrologue = 1 << 18, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). - NoHoisting = 1 << 19, // Do not hoist this declaration in --module system - HasEndOfDeclarationMarker = 1 << 20, // Declaration has an associated NotEmittedStatement to mark the end of the declaration + HelperName = 1 << 12, + ExportName = 1 << 13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). + LocalName = 1 << 14, // Ensure an export prefix is not added for an identifier that points to an exported declaration. + Indented = 1 << 15, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). + NoIndentation = 1 << 16, // Do not indent the node. + AsyncFunctionBody = 1 << 17, + ReuseTempVariableScope = 1 << 18, // Reuse the existing temp variable scope during emit. + CustomPrologue = 1 << 19, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). + NoHoisting = 1 << 20, // Do not hoist this declaration in --module system + HasEndOfDeclarationMarker = 1 << 21, // Declaration has an associated NotEmittedStatement to mark the end of the declaration } /* @internal */ @@ -3568,16 +3574,123 @@ namespace ts { Unspecified, // Emitting an otherwise unspecified node } - /** Additional context provided to `visitEachChild` */ /* @internal */ - export interface LexicalEnvironment { + export interface EmitHost extends ScriptReferenceHost { + getSourceFiles(): SourceFile[]; + + /* @internal */ + isSourceFileFromExternalLibrary(file: SourceFile): boolean; + + getCommonSourceDirectory(): string; + getCanonicalFileName(fileName: string): string; + getNewLine(): string; + + isEmitBlocked(emitFileName: string): boolean; + + writeFile: WriteFileCallback; + } + + /* @internal */ + export interface TransformationContext { + getCompilerOptions(): CompilerOptions; + getEmitResolver(): EmitResolver; + getEmitHost(): EmitHost; + /** Starts a new lexical environment. */ startLexicalEnvironment(): void; + /** Suspends the current lexical environment, usually after visiting a parameter list. */ + suspendLexicalEnvironment(): void; + + /** Resumes a suspended lexical environment, usually before visiting a function body. */ + resumeLexicalEnvironment(): void; + /** Ends a lexical environment, returning any declarations. */ endLexicalEnvironment(): Statement[]; + + /** + * Hoists a function declaration to the containing scope. + */ + hoistFunctionDeclaration(node: FunctionDeclaration): void; + + /** + * Hoists a variable declaration to the containing scope. + */ + hoistVariableDeclaration(node: Identifier): void; + + /** + * Records a request for a non-scoped emit helper in the current context. + */ + requestEmitHelper(helper: EmitHelper): void; + + /** + * Gets and resets the requested non-scoped emit helpers. + */ + readEmitHelpers(): EmitHelper[] | undefined; + + /** + * Enables expression substitutions in the pretty printer for the provided SyntaxKind. + */ + enableSubstitution(kind: SyntaxKind): void; + + /** + * Determines whether expression substitutions are enabled for the provided node. + */ + isSubstitutionEnabled(node: Node): boolean; + + /** + * Hook used by transformers to substitute expressions just before they + * are emitted by the pretty printer. + */ + onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node; + + /** + * Enables before/after emit notifications in the pretty printer for the provided + * SyntaxKind. + */ + enableEmitNotification(kind: SyntaxKind): void; + + /** + * Determines whether before/after emit notifications should be raised in the pretty + * printer when it emits a node. + */ + isEmitNotificationEnabled(node: Node): boolean; + + /** + * Hook used to allow transformers to capture state before or after + * the printer emits a node. + */ + onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void; } + /* @internal */ + export interface TransformationResult { + /** + * Gets the transformed source files. + */ + transformed: SourceFile[]; + + /** + * Emits the substitute for a node, if one is available; otherwise, emits the node. + * + * @param emitContext The current emit context. + * @param node The node to substitute. + * @param emitCallback A callback used to emit the node or its substitute. + */ + emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; + + /** + * Emits a node with possible notification. + * + * @param emitContext The current emit context. + * @param node The node to emit. + * @param emitCallback A callback used to emit the node. + */ + emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; + } + + /* @internal */ + export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile; export interface TextSpan { start: number; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 79e15566e1d..5bdcf27769f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -28,21 +28,6 @@ namespace ts { string(): string; } - export interface EmitHost extends ScriptReferenceHost { - getSourceFiles(): SourceFile[]; - - /* @internal */ - isSourceFileFromExternalLibrary(file: SourceFile): boolean; - - getCommonSourceDirectory(): string; - getCanonicalFileName(fileName: string): string; - getNewLine(): string; - - isEmitBlocked(emitFileName: string): boolean; - - writeFile: WriteFileCallback; - } - // Pool writers to avoid needing to allocate them for every symbol we write. const stringWriters: StringSymbolWriter[] = []; export function getSingleLineStringWriter(): StringSymbolWriter { @@ -595,8 +580,9 @@ namespace ts { return n.kind === SyntaxKind.CallExpression && (n).expression.kind === SyntaxKind.SuperKeyword; } - export function isPrologueDirective(node: Node): boolean { - return node.kind === SyntaxKind.ExpressionStatement && (node).expression.kind === SyntaxKind.StringLiteral; + export function isPrologueDirective(node: Node): node is PrologueDirective { + return node.kind === SyntaxKind.ExpressionStatement + && (node).expression.kind === SyntaxKind.StringLiteral; } export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode: SourceFile) { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 609031a8221..dc0e69a9b0f 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -653,6 +653,53 @@ namespace ts { return updated || nodes; } + /** + * Starts a new lexical environment and visits a statement list, ending the lexical environment + * and merging hoisted declarations upon completion. + */ + export function visitLexicalEnvironment(statements: NodeArray, visitor: (node: Node) => VisitResult, context: TransformationContext, start?: number, ensureUseStrict?: boolean) { + context.startLexicalEnvironment(); + statements = visitNodes(statements, visitor, isStatement, start); + if (ensureUseStrict && !startsWithUseStrict(statements)) { + statements = createNodeArray([createStatement(createLiteral("use strict")), ...statements], statements); + } + statements = mergeLexicalEnvironment(statements, context.endLexicalEnvironment()); + return statements; + } + + /** + * Starts a new lexical environment and visits a parameter list, suspending the lexical + * environment upon completion. + */ + export function visitParameterList(nodes: NodeArray, visitor: (node: Node) => VisitResult, context: TransformationContext) { + context.startLexicalEnvironment(); + const updated = visitNodes(nodes, visitor, isParameterDeclaration); + context.suspendLexicalEnvironment(); + return updated; + } + + /** + * Resumes a suspended lexical environment and visits a function body, ending the lexical + * environment and merging hoisted declarations upon completion. + */ + export function visitFunctionBody(node: FunctionBody, visitor: (node: Node) => VisitResult, context: TransformationContext, optional?: boolean): FunctionBody; + /** + * Resumes a suspended lexical environment and visits a concise body, ending the lexical + * environment and merging hoisted declarations upon completion. + */ + export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult, context: TransformationContext): ConciseBody; + export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult, context: TransformationContext, optional?: boolean): ConciseBody { + context.resumeLexicalEnvironment(); + const updated = visitNode(node, visitor, isConciseBody, optional); + const declarations = context.endLexicalEnvironment(); + if (some(declarations)) { + const block = convertToFunctionBody(updated); + const statements = mergeLexicalEnvironment(block.statements, declarations); + return updateBlock(block, statements); + } + return updated; + } + /** * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place. * @@ -660,8 +707,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) => VisitResult, context: LexicalEnvironment): T; - export function visitEachChild(node: Node, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): Node { + export function visitEachChild(node: T, visitor: (node: Node) => VisitResult, context: TransformationContext): T; + export function visitEachChild(node: Node, visitor: (node: Node) => VisitResult, context: TransformationContext): Node { if (node === undefined) { return undefined; } @@ -714,41 +761,33 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); case SyntaxKind.Constructor: return updateConstructor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitParameterList((node).parameters, visitor, context), + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); case SyntaxKind.GetAccessor: return updateGetAccessor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); case SyntaxKind.SetAccessor: return updateSetAccessor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitParameterList((node).parameters, visitor, context), + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); // Binding patterns case SyntaxKind.ObjectBindingPattern: @@ -810,21 +849,17 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); case SyntaxKind.ArrowFunction: return updateArrowFunction(node, visitNodes((node).modifiers, visitor, isModifier), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isConciseBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.DeleteExpression: return updateDelete(node, @@ -995,11 +1030,9 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); case SyntaxKind.ClassDeclaration: return updateClassDeclaration(node, @@ -1129,11 +1162,7 @@ namespace ts { case SyntaxKind.SourceFile: context.startLexicalEnvironment(); return updateSourceFileNode(node, - createNodeArray( - concatenate( - visitNodes((node).statements, visitor, isStatement), - context.endLexicalEnvironment()), - (node).statements)); + visitLexicalEnvironment((node).statements, visitor, context)); // Transformation nodes case SyntaxKind.PartiallyEmittedExpression: @@ -1167,6 +1196,24 @@ namespace ts { // return node; } + /** + * Merges generated lexical declarations into a new statement list. + */ + export function mergeLexicalEnvironment(statements: NodeArray, declarations: Statement[]): NodeArray; + /** + * Appends generated lexical declarations to an array of statements. + */ + export function mergeLexicalEnvironment(statements: Statement[], declarations: Statement[]): Statement[]; + export function mergeLexicalEnvironment(statements: Statement[], declarations: Statement[]) { + if (!some(declarations)) { + return statements; + } + return isNodeArray(statements) + ? createNodeArray(concatenate(statements, declarations), statements) + : addRange(statements, declarations); + } + + /** * Merges generated lexical declarations into the FunctionBody of a non-arrow function-like declaration. * From 8babde0b9839fbd500a3089abd33754f478a05d2 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 11 Nov 2016 17:23:42 -0800 Subject: [PATCH 04/26] WIP Clean up destructuring --- src/compiler/core.ts | 7 + src/compiler/factory.ts | 14 +- src/compiler/transformers/destructuring.ts | 758 ++++++++++++++++++++- src/compiler/transformers/esnext.ts | 2 +- src/compiler/types.ts | 10 +- src/compiler/utilities.ts | 26 +- src/compiler/visitor.ts | 30 + 7 files changed, 814 insertions(+), 33 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 8175730159b..a21477a40d6 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -811,6 +811,13 @@ namespace ts { } } + export function appendProperty(map: Map, key: string | number, value: T): Map { + if (key === undefined || value === undefined) return map; + if (map === undefined) map = createMap(); + map[key] = value; + return map; + } + export function assign, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3; export function assign, T2>(t: T1, arg1: T2): T1 & T2; export function assign>(t: T1, ...args: any[]): any; diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index e9935668676..6674ffc7263 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -238,9 +238,9 @@ namespace ts { return node; } - export function updateParameter(node: ParameterDeclaration, decorators: Decorator[], modifiers: Modifier[], name: BindingName, type: TypeNode, initializer: Expression) { - if (node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name || node.type !== type || node.initializer !== initializer) { - return updateNode(createParameter(decorators, modifiers, node.dotDotDotToken, name, node.questionToken, type, initializer, /*location*/ node, /*flags*/ node.flags), node); + export function updateParameter(node: ParameterDeclaration, decorators: Decorator[], modifiers: Modifier[], dotDotDotToken: DotDotDotToken, name: BindingName, type: TypeNode, initializer: Expression) { + if (node.decorators !== decorators || node.modifiers !== modifiers || node.dotDotDotToken !== dotDotDotToken || node.name !== name || node.type !== type || node.initializer !== initializer) { + return updateNode(createParameter(decorators, modifiers, dotDotDotToken, name, node.questionToken, type, initializer, /*location*/ node, /*flags*/ node.flags), node); } return node; @@ -378,9 +378,9 @@ namespace ts { return node; } - export function updateBindingElement(node: BindingElement, propertyName: PropertyName, name: BindingName, initializer: Expression) { - if (node.propertyName !== propertyName || node.name !== name || node.initializer !== initializer) { - return updateNode(createBindingElement(propertyName, node.dotDotDotToken, name, initializer, node), node); + export function updateBindingElement(node: BindingElement, dotDotDotToken: DotDotDotToken, propertyName: PropertyName, name: BindingName, initializer: Expression) { + if (node.propertyName !== propertyName || node.dotDotDotToken !== dotDotDotToken || node.name !== name || node.initializer !== initializer) { + return updateNode(createBindingElement(propertyName, dotDotDotToken, name, initializer, node), node); } return node; } @@ -1635,7 +1635,7 @@ namespace ts { // flag and setting a parent node. const react = createIdentifier(reactNamespace || "React"); react.flags &= ~NodeFlags.Synthesized; - // Set the parent that is in parse tree + // Set the parent that is in parse tree // this makes sure that parent chain is intact for checker to traverse complete scope tree react.parent = getParseTreeNode(parent); return react; diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index bba09710898..b0cb3f9d239 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -3,6 +3,18 @@ /*@internal*/ namespace ts { + type EffectiveBindingElement = VariableDeclaration | ParameterDeclaration | BindingElement | ObjectLiteralElementLike | Expression; + type EffectiveObjectBindingPattern = ObjectBindingPattern | ObjectLiteralExpression; + type EffectiveArrayBindingPattern = ArrayBindingPattern | ArrayLiteralExpression; + type EffectiveBindingPattern = EffectiveObjectBindingPattern | EffectiveArrayBindingPattern; + type EffectiveBindingTarget = EffectiveBindingPattern | Expression; + type EffectiveRestIndicator = DotDotDotToken | SpreadElement | SpreadAssignment; + + export const enum FlattenLevel { + All, + ObjectRestDestructuringOnly, + } + /** * Flattens a destructuring assignment expression. * @@ -40,7 +52,7 @@ namespace ts { // // The source map location for the assignment should point to the entire binary // expression. - value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment, visitor); + value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment, visitor); } else if (nodeIsSynthesized(node)) { // Generally, the source map location for a destructuring assignment is the root @@ -52,7 +64,7 @@ namespace ts { location = value; } - flattenDestructuring(node, value, location, emitAssignment, emitTempVariableAssignment, recordTempVariable, emitRestAssignment, transformRest, visitor); + flattenDestructuringOld(node, value, location, emitAssignment, emitTempVariableAssignment, recordTempVariable, emitRestAssignment, transformRest, visitor); if (needsValue) { expressions.push(value); @@ -98,7 +110,7 @@ namespace ts { transformRest?: boolean) { const declarations: VariableDeclaration[] = []; - flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment, noop, emitRestAssignment, transformRest, visitor); + flattenDestructuringOld(node, value, node, emitAssignment, emitTempVariableAssignment, noop, emitRestAssignment, transformRest, visitor); return declarations; @@ -140,7 +152,7 @@ namespace ts { const declarations: VariableDeclaration[] = []; let pendingAssignments: Expression[]; - flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment, recordTempVariable, emitRestAssignment, transformRest, visitor); + flattenDestructuringOld(node, value, node, emitAssignment, emitTempVariableAssignment, recordTempVariable, emitRestAssignment, transformRest, visitor); return declarations; @@ -201,7 +213,7 @@ namespace ts { const pendingAssignments: Expression[] = []; - flattenDestructuring(node, /*value*/ undefined, node, emitAssignment, emitTempVariableAssignment, noop, emitRestAssignment, /*transformRest*/ false, visitor); + flattenDestructuringOld(node, /*value*/ undefined, node, emitAssignment, emitTempVariableAssignment, noop, emitRestAssignment, /*transformRest*/ false, visitor); const expression = inlineExpressions(pendingAssignments); aggregateTransformFlags(expression); @@ -238,7 +250,7 @@ namespace ts { } } - function flattenDestructuring( + function flattenDestructuringOld( root: VariableDeclaration | ParameterDeclaration | BindingElement | BinaryExpression, value: Expression, location: TextRange, @@ -305,7 +317,7 @@ namespace ts { // 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); + value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); } let bindingElements: ObjectLiteralElementLike[] = []; @@ -361,7 +373,7 @@ namespace ts { // 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 highlight the passed-in source map node since thats the one needing this temp assignment - value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); + value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); } const expressions: Expression[] = []; @@ -393,7 +405,7 @@ namespace ts { // 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 highlight the passed-in source map node since thats the one needing this temp assignment - value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); + value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); } for (let i = 0; i < numElements; i++) { @@ -454,7 +466,7 @@ namespace ts { // 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); + value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ numElements !== 0, target, emitTempVariableAssignment); } if (name.kind === SyntaxKind.ArrayBindingPattern) { emitArrayBindingElement(name as ArrayBindingPattern, value); @@ -558,7 +570,7 @@ namespace ts { } function createDefaultValueCheck(value: Expression, defaultValue: Expression, location: TextRange): Expression { - value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); + value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); return createConditional( createStrictEquality(value, createVoidZero()), createToken(SyntaxKind.QuestionToken), @@ -579,7 +591,7 @@ namespace ts { if (isComputedPropertyName(propertyName)) { return createElementAccess( expression, - ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, /*location*/ propertyName, emitTempVariableAssignment) + ensureIdentifierOld(propertyName.expression, /*reuseIdentifierExpressions*/ false, /*location*/ propertyName, emitTempVariableAssignment) ); } else if (isLiteralExpression(propertyName)) { @@ -612,7 +624,7 @@ namespace ts { * @param emitTempVariableAssignment A callback used to emit a temporary variable. * @param visitor An optional callback used to visit the value. */ - function ensureIdentifier( + function ensureIdentifierOld( value: Expression, reuseIdentifierExpressions: boolean, location: TextRange, @@ -630,4 +642,724 @@ namespace ts { return emitTempVariableAssignment(value, location); } } + + interface FlattenHost { + context: TransformationContext; + level: FlattenLevel; + recordTempVariablesInLine: boolean; + emitAssignment: (target: EffectiveBindingTarget, value: Expression, location: TextRange, original: Node) => void; + emitArrayAssignment: (elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) => void; + emitObjectAssignment: (elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) => void; + emitExpression: (value: Expression) => void; + } + + export function flattenDestructuringToDeclarations( + context: TransformationContext, + node: VariableDeclaration | ParameterDeclaration, + boundValue: Expression | undefined, + recordTempVariablesInLine: boolean, + level: FlattenLevel): VisitResult { + + const pendingDeclarations: { pendingExpressions?: Expression[], name: BindingName, value: Expression, location?: TextRange, original?: Node; }[] = []; + let pendingExpressions: Expression[]; + let declaration: VariableDeclaration; + let declarations: VariableDeclaration[]; + const host: FlattenHost = { + context, + level, + recordTempVariablesInLine, + emitExpression, + emitAssignment, + emitArrayAssignment, + emitObjectAssignment + }; + + flattenEffectiveBindingElement(host, node, boundValue, node); + + if (pendingExpressions) { + const temp = createTempVariable(/*recordTempVariable*/ undefined); + if (recordTempVariablesInLine) { + const value = inlineExpressions(pendingExpressions); + pendingExpressions = undefined; + emitAssignment(temp, value, /*location*/ undefined, /*original*/ undefined); + } + else { + context.hoistVariableDeclaration(temp); + const pendingDeclaration = lastOrUndefined(pendingDeclarations); + pendingDeclaration.pendingExpressions = append( + pendingDeclaration.pendingExpressions, + createAssignment(temp, pendingDeclaration.value) + ); + addRange(pendingDeclaration.pendingExpressions, pendingExpressions); + pendingDeclaration.value = temp; + } + } + + for (const { pendingExpressions, name, value, location, original } of pendingDeclarations) { + const variable = createVariableDeclaration( + name, + /*type*/ undefined, + pendingExpressions ? inlineExpressions(append(pendingExpressions, value)) : value, + location); + variable.original = original; + if (isIdentifier(name)) { + setEmitFlags(variable, EmitFlags.NoNestedSourceMaps); + } + aggregateTransformFlags(variable); + if (!declaration) { + declaration = variable; + } + else if (!declarations) { + declarations = [declaration, variable]; + } + else { + declarations.push(variable); + } + } + + return declarations || declaration; + + function emitExpression(value: Expression) { + pendingExpressions = append(pendingExpressions, value); + } + + function emitAssignment(target: EffectiveBindingTarget, value: Expression, location: TextRange, original: Node) { + Debug.assertNode(target, isBindingName); + pendingDeclarations.push({ pendingExpressions, name: target, value, location, original }); + if (pendingExpressions) { + value = inlineExpressions(append(pendingExpressions, value)); + pendingExpressions = undefined; + } + } + + function emitArrayAssignment(elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) { + Debug.assertEachNode(elements, isArrayBindingElement); + emitAssignment(createArrayBindingPattern(elements), value, location, original); + } + + function emitObjectAssignment(elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) { + Debug.assertEachNode(elements, isBindingElement); + emitAssignment(createObjectBindingPattern(elements), value, location, original); + } + } + + function flattenEffectiveBindingElement( + host: FlattenHost, + bindingElement: EffectiveBindingElement, + boundValue: Expression | undefined, + location: TextRange, + skipInitializer?: boolean) { + if (!skipInitializer) { + const initializer = getInitializerOfEffectiveBindingElement(bindingElement); + if (initializer) { + // Combine value and initializer + boundValue = boundValue ? createDefaultValueCheck(host, boundValue, initializer, location) : initializer; + } + else if (!boundValue) { + // Use 'void 0' in absence of value and initializer + boundValue = createVoidZero(); + } + } + const bindingTarget = getTargetOfEffectiveBindingElement(bindingElement); + if (!isEffectiveBindingPattern(bindingTarget)) { + host.emitAssignment(bindingTarget, boundValue, location, /*original*/ bindingElement); + } + else { + const elements = getElementsOfEffectiveBindingPattern(bindingTarget); + 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. + const reuseIdentifierExpressions = !isDeclarationBindingElement(bindingElement) || numElements !== 0; + boundValue = ensureIdentifier(host, boundValue, reuseIdentifierExpressions, location); + } + if (isEffectiveObjectBindingPattern(bindingTarget)) { + flattenEffectiveObjectBindingElements(host, bindingTarget, elements, boundValue, location); + } + else { + flattenEffectiveArrayBindingElements(host, bindingTarget, elements, boundValue, location); + } + } + } + + function flattenEffectiveObjectBindingElements(host: FlattenHost, bindingTarget: EffectiveObjectBindingPattern, elements: EffectiveBindingElement[], boundValue: Expression, location: TextRange) { + let bindingElements: EffectiveBindingElement[]; + const numElements = elements.length; + for (let i = 0; i < numElements; i++) { + const element = elements[i]; + if (!getEffectiveRestIndicator(element)) { + if (host.level >= FlattenLevel.ObjectRestDestructuringOnly && !(element.transformFlags & TransformFlags.ContainsSpreadExpression)) { + bindingElements = append(bindingElements, element); + } + else { + if (bindingElements) { + host.emitObjectAssignment(bindingElements, boundValue, location, bindingTarget); + bindingElements = undefined; + } + const propertyName = getEffectivePropertyNameOfEffectiveBindingElement(element); + const value = createDestructuringPropertyAccess(host, boundValue, propertyName); + flattenEffectiveBindingElement(host, element, value, /*location*/ element); + } + } + else if (i === numElements - 1) { + if (bindingElements) { + host.emitObjectAssignment(bindingElements, boundValue, location, bindingTarget); + bindingElements = undefined; + } + const value = createRestCall(boundValue, elements, bindingTarget); + flattenEffectiveBindingElement(host, element, value, element); + } + } + if (bindingElements) { + host.emitObjectAssignment(bindingElements, boundValue, location, bindingTarget); + } + } + + function flattenEffectiveArrayBindingElements(host: FlattenHost, bindingTarget: EffectiveArrayBindingPattern, elements: EffectiveBindingElement[], boundValue: Expression, location: TextRange) { + let bindingElements: EffectiveBindingElement[]; + let spreadContainingElements: EffectiveBindingElement[]; + let tempNames: Map; + const numElements = elements.length; + for (let i = 0; i < numElements; i++) { + const element = elements[i]; + if (isOmittedExpression(element)) { + continue; + } + else if (host.level >= FlattenLevel.ObjectRestDestructuringOnly) { + if (element.transformFlags & TransformFlags.ContainsSpreadExpression) { + const temp = createTempVariable(/*recordTempVariable*/ undefined); + tempNames = appendProperty(tempNames, getNodeId(element), temp); + spreadContainingElements = append(spreadContainingElements, element); + bindingElements = append(bindingElements, temp); + } + else { + bindingElements = append(bindingElements, element); + } + } + else if (!getEffectiveRestIndicator(element)) { + const value = createElementAccess(boundValue, i); + flattenEffectiveBindingElement(host, element, value, /*location*/ element); + } + else if (i === numElements - 1) { + const value = createArraySlice(boundValue, i); + flattenEffectiveBindingElement(host, element, value, /*location*/ element); + } + } + if (bindingElements) { + host.emitArrayAssignment(bindingElements, boundValue, location, bindingTarget); + } + if (spreadContainingElements) { + for (const element of spreadContainingElements) { + flattenEffectiveBindingElement(host, element, tempNames[getNodeId(element)], element); + } + } + } + + /** + * Creates an expression used to provide a default value if a value is `undefined` at runtime. + */ + function createDefaultValueCheck(host: FlattenHost, value: Expression, defaultValue: Expression, location: TextRange): Expression { + value = ensureIdentifier(host, value, /*reuseIdentifierExpressions*/ true, location); + return createConditional( + createStrictEquality(value, createVoidZero()), + createToken(SyntaxKind.QuestionToken), + defaultValue, + createToken(SyntaxKind.ColonToken), + value + ); + } + + /** + * Creates either a PropertyAccessExpression or an ElementAccessExpression for the + * right-hand side of a transformed destructuring assignment. + * + * @param expression The right-hand expression that is the source of the property. + * @param propertyName The destructuring property name. + * @param emitTempVariableAssignment A callback used to emit a temporary variable. + */ + function createDestructuringPropertyAccess(host: FlattenHost, expression: Expression, propertyName: PropertyName): LeftHandSideExpression { + if (isComputedPropertyName(propertyName)) { + const argumentExpression = ensureIdentifier(host, propertyName.expression, /*reuseIdentifierExpressions*/ false, /*location*/ propertyName); + return createElementAccess(expression, argumentExpression); + } + else if (isLiteralExpression(propertyName)) { + const argumentExpression = getSynthesizedClone(propertyName); + argumentExpression.text = unescapeIdentifier(argumentExpression.text); + return createElementAccess(expression, argumentExpression); + } + else if (isGeneratedIdentifier(propertyName)) { + const name = getSynthesizedClone(propertyName); + name.text = unescapeIdentifier(name.text); + return createPropertyAccess(expression, name); + } + else { + const name = createIdentifier(unescapeIdentifier(propertyName.text)); + return createPropertyAccess(expression, name); + } + } + + /** + * 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 emitTempVariableAssignment A callback used to emit a temporary variable. + * @param location The location to use for source maps and comments. + */ + function ensureIdentifier( + host: FlattenHost, + value: Expression, + reuseIdentifierExpressions: boolean, + location: TextRange) { + + if (isIdentifier(value) && reuseIdentifierExpressions) { + return value; + } + else { + const temp = createTempVariable(/*recordTempVariable*/ undefined); + if (host.recordTempVariablesInLine) { + host.emitAssignment(temp, value, location, /*original*/ undefined); + } + else { + host.context.hoistVariableDeclaration(temp); + host.emitExpression(createAssignment(temp, value, location)); + } + return temp; + } + } + + /** + * Determines whether the EffectiveBindingElement is a declaration + */ + function isDeclarationBindingElement(bindingElement: EffectiveBindingElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement { + switch (bindingElement.kind) { + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.BindingElement: + return true; + } + + return false; + } + + /** + * Gets the initializer of an EffectiveBindingElement. + */ + function getInitializerOfEffectiveBindingElement(bindingElement: EffectiveBindingElement): Expression | undefined { + if (isDeclarationBindingElement(bindingElement)) { + // `1` in `let { a = 1 } = ...` + // `1` in `let { a: b = 1 } = ...` + // `1` in `let { a: {b} = 1 } = ...` + // `1` in `let { a: [b] = 1 } = ...` + // `1` in `let [a = 1] = ...` + // `1` in `let [{a} = 1] = ...` + // `1` in `let [[a] = 1] = ...` + return bindingElement.initializer; + } + + if (isPropertyAssignment(bindingElement)) { + // `1` in `({ a: b = 1 } = ...)` + // `1` in `({ a: {b} = 1 } = ...)` + // `1` in `({ a: [b] = 1 } = ...)` + return isAssignmentExpression(bindingElement.initializer, /*excludeCompoundAssignment*/ true) + ? bindingElement.initializer.right + : undefined; + } + + if (isShorthandPropertyAssignment(bindingElement)) { + // `1` in `({ a = 1 } = ...)` + return bindingElement.objectAssignmentInitializer; + } + + if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { + // `1` in `[a = 1] = ...` + // `1` in `[{a} = 1] = ...` + // `1` in `[[a] = 1] = ...` + return bindingElement.right; + } + + if (isSpreadExpression(bindingElement) || isPartiallyEmittedExpression(bindingElement)) { + // Recovery consistent with existing emit. + return getInitializerOfEffectiveBindingElement(bindingElement.expression); + } + } + + /** + * Gets the name of an EffectiveBindingElement. + */ + function getTargetOfEffectiveBindingElement(bindingElement: EffectiveBindingElement): EffectiveBindingTarget { + if (isDeclarationBindingElement(bindingElement)) { + // `a` in `let { a } = ...` + // `a` in `let { a = 1 } = ...` + // `b` in `let { a: b } = ...` + // `b` in `let { a: b = 1 } = ...` + // `a` in `let { ...a } = ...` + // `{b}` in `let { a: {b} } = ...` + // `{b}` in `let { a: {b} = 1 } = ...` + // `[b]` in `let { a: [b] } = ...` + // `[b]` in `let { a: [b] = 1 } = ...` + // `a` in `let [a] = ...` + // `a` in `let [a = 1] = ...` + // `a` in `let [...a] = ...` + // `{a}` in `let [{a}] = ...` + // `{a}` in `let [{a} = 1] = ...` + // `[a]` in `let [[a]] = ...` + // `[a]` in `let [[a] = 1] = ...` + return bindingElement.name; + } + + if (isObjectLiteralElementLike(bindingElement)) { + switch (bindingElement.kind) { + case SyntaxKind.PropertyAssignment: + // `b` in `({ a: b } = ...)` + // `b` in `({ a: b = 1 } = ...)` + // `{b}` in `({ a: {b} } = ...)` + // `{b}` in `({ a: {b} = 1 } = ...)` + // `[b]` in `({ a: [b] } = ...)` + // `[b]` in `({ a: [b] = 1 } = ...)` + // `b.c` in `({ a: b.c } = ...)` + // `b.c` in `({ a: b.c = 1 } = ...)` + // `b[0]` in `({ a: b[0] } = ...)` + // `b[0]` in `({ a: b[0] = 1 } = ...)` + return getTargetOfEffectiveBindingElement(bindingElement.initializer); + + case SyntaxKind.ShorthandPropertyAssignment: + // `a` in `({ a } = ...)` + // `a` in `({ a = 1 } = ...)` + return bindingElement.name; + + case SyntaxKind.SpreadAssignment: + // `a` in `({ ...a } = ...)` + return getTargetOfEffectiveBindingElement(bindingElement.expression); + } + + // no target + return undefined; + } + + if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { + // `a` in `[a = 1] = ...` + // `{a}` in `[{a} = 1] = ...` + // `[a]` in `[[a] = 1] = ...` + // `a.b` in `[a.b = 1] = ...` + // `a[0]` in `[a[0] = 1] = ...` + return getTargetOfEffectiveBindingElement(bindingElement.left); + } + + if (isSpreadExpression(bindingElement) || isPartiallyEmittedExpression(bindingElement)) { + // `a` in `[...a] = ...` + return getTargetOfEffectiveBindingElement(bindingElement.expression); + } + + // `a` in `[a] = ...` + // `{a}` in `[{a}] = ...` + // `[a]` in `[[a]] = ...` + // `a.b` in `[a.b] = ...` + // `a[0]` in `[a[0]] = ...` + return bindingElement; + } + + /** + * Determines whether an EffectiveBindingElement is a rest element. + */ + function getEffectiveRestIndicator(bindingElement: EffectiveBindingElement): EffectiveRestIndicator { + switch (bindingElement.kind) { + case SyntaxKind.Parameter: + case SyntaxKind.BindingElement: + // `...` in `let [...a] = ...` + return (bindingElement).dotDotDotToken; + + case SyntaxKind.SpreadElement: + case SyntaxKind.SpreadAssignment: + // `...` in `[...a] = ...` + return bindingElement; + } + + return undefined; + } + + /** + * Gets the property name of a BindingElement-like element + */ + function getEffectivePropertyNameOfEffectiveBindingElement(bindingElement: EffectiveBindingElement) { + switch (bindingElement.kind) { + case SyntaxKind.BindingElement: + // `a` in `let { a: b } = ...` + // `[a]` in `let { [a]: b } = ...` + // `"a"` in `let { "a": b } = ...` + // `1` in `let { 1: b } = ...` + if ((bindingElement).propertyName) { + return (bindingElement).propertyName; + } + + break; + + case SyntaxKind.PropertyAssignment: + // `a` in `({ a: b } = ...)` + // `[a]` in `({ [a]: b } = ...)` + // `"a"` in `({ "a": b } = ...)` + // `1` in `({ 1: b } = ...)` + if ((bindingElement).name) { + return (bindingElement).name; + } + + break; + + case SyntaxKind.SpreadAssignment: + // `a` in `({ ...a } = ...)` + return (bindingElement).name; + } + + const target = getTargetOfEffectiveBindingElement(bindingElement); + if (target && isPropertyName(target)) { + return target; + } + + Debug.fail("Invalid property name for binding element."); + } + + /** + * Determines whether a node is BindingPattern-like + */ + function isEffectiveBindingPattern(node: EffectiveBindingTarget): node is EffectiveBindingPattern { + return isEffectiveObjectBindingPattern(node) + || isEffectiveArrayBindingPattern(node); + } + + /** + * Determines whether a node is ObjectBindingPattern-like + */ + function isEffectiveObjectBindingPattern(node: EffectiveBindingTarget): node is EffectiveObjectBindingPattern { + switch (node.kind) { + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ObjectLiteralExpression: + return true; + } + + return false; + } + + /** + * Determines whether a node is ArrayBindingPattern-like + */ + function isEffectiveArrayBindingPattern(node: EffectiveBindingTarget): node is EffectiveArrayBindingPattern { + switch (node.kind) { + case SyntaxKind.ArrayBindingPattern: + case SyntaxKind.ArrayLiteralExpression: + return true; + } + + return false; + } + + /** + * Gets the elements of a BindingPattern-like name + */ + function getElementsOfEffectiveBindingPattern(name: EffectiveBindingPattern): EffectiveBindingElement[] { + switch (name.kind) { + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + case SyntaxKind.ArrayLiteralExpression: + // `a` in `{a}` + // `a` in `[a]` + return name.elements; + + case SyntaxKind.ObjectLiteralExpression: + // `a` in `{a}` + return name.properties; + } + } + + // function updateEffectiveBindingElement(bindingElement: EffectiveBindingElement, restIndicator: EffectiveRestIndicator, propertyName: PropertyName, target: EffectiveBindingTarget, initializer: Expression) { + // switch (bindingElement.kind) { + // case SyntaxKind.VariableDeclaration: + // Debug.assertNode(target, isBindingName); + // Debug.assertMissingNode(propertyName); + // return updateVariableDeclaration( + // bindingElement, + // target, + // /*type*/ undefined, + // initializer + // ); + + // case SyntaxKind.Parameter: + // Debug.assertOptionalToken(restIndicator, SyntaxKind.DotDotDotToken); + // Debug.assertMissingNode(propertyName); + // Debug.assertNode(target, isBindingName); + // return updateParameter( + // bindingElement, + // /*decorators*/ undefined, + // /*modifiers*/ undefined, + // restIndicator, + // target, + // /*type*/ undefined, + // initializer + // ); + + // case SyntaxKind.BindingElement: + // Debug.assertOptionalToken(restIndicator, SyntaxKind.DotDotDotToken); + // Debug.assertNode(target, isBindingName); + // return updateBindingElement( + // bindingElement, + // restIndicator, + // propertyName, + // target, + // initializer + // ); + + // case SyntaxKind.PropertyAssignment: + // case SyntaxKind.ShorthandPropertyAssignment: + // case SyntaxKind.SpreadAssignment: + // if (restIndicator) { + // return convertToSpreadAssignment(bindingElement, propertyName, target, initializer); + // } + // else if (propertyName) { + // return convertToPropertyAssignment(bindingElement, propertyName, target, initializer); + // } + // else { + // return convertToShorthandPropertyAssignment(bindingElement, target, initializer); + // } + + // case SyntaxKind.ArrayLiteralExpression: + // case SyntaxKind.BinaryExpression: + // case SyntaxKind.SpreadElement: + // case SyntaxKind.PropertyAccessExpression: + // case SyntaxKind.ElementAccessExpression: + // Debug.assertMissingNode(propertyName); + // if (restIndicator) { + // return convertToSpreadElement(bindingElement, target, initializer); + // } + // else { + + // } + // } + // } + + // function convertToSpreadAssignment(node: PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment, propertyName: PropertyName, target: EffectiveBindingTarget, initializer: Expression) { + // Debug.assertMissingNode(propertyName); + // Debug.assertNode(target, isIdentifier); + // Debug.assertMissingNode(initializer); + // if (node.kind === SyntaxKind.SpreadAssignment) { + // return updateSpreadAssignment(node, target); + // } + // return setOriginalNode( + // createSpreadAssignment( + // target, + // node + // ), + // node + // ); + // } + + // function convertToPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment, propertyName: PropertyName, target: EffectiveBindingTarget, initializer: Expression) { + // Debug.assertNode(target, isExpression); + // if (node.kind === SyntaxKind.PropertyAssignment) { + // return updatePropertyAssignment( + // node, + // propertyName, + // initializer ? + // isAssignmentExpression(node.initializer, /*excludeCompoundAssignment*/ true) + // ? updateBinary(node.initializer, target, initializer) + // : createAssignment(target, initializer) + // : target + // ); + // } + // return setOriginalNode( + // createPropertyAssignment( + // propertyName, + // initializer ? + // createAssignment(target, initializer) + // : target, + // node + // ), + // node + // ); + // } + + // function convertToShorthandPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment, target: EffectiveBindingTarget, initializer: Expression) { + // Debug.assertNode(target, isIdentifier); + // if (node.kind === SyntaxKind.ShorthandPropertyAssignment) { + // return updateShorthandPropertyAssignment( + // node, + // target, + // initializer + // ); + // } + // return setOriginalNode( + // createShorthandPropertyAssignment( + // target, + // initializer, + // node + // ), + // node + // ); + // } + + // function convertToSpreadElement(node: Expression, target: EffectiveBindingTarget, initializer: Expression) { + // Debug.assertNode(target, isExpression); + // Debug.assertMissingNode(initializer); + // if (node.kind === SyntaxKind.SpreadElement) { + // return updateSpread( + // node, + // target); + // } + // return setOriginalNode( + // createSpread( + // target, + // node + // ), + // node + // ); + // } + + // function convertToElement(node: Expression, target: EffectiveBindingTarget, initializer: Expression) { + + // } + + function createOrUpdateVariableDeclaration(node: Node, name: BindingName, initializer: Expression, location: TextRange) { + if (node && node.kind === SyntaxKind.VariableDeclaration) { + return updateVariableDeclaration(node, name, /*type*/ undefined, initializer); + } + const variable = createVariableDeclaration(name, /*type*/ undefined, initializer, location); + return node ? setOriginalNode(variable, node) : variable; + } + + function createOrUpdateArrayBindingPattern(node: Node, elements: ArrayBindingElement[], location: TextRange) { + if (node && node.kind === SyntaxKind.ArrayBindingPattern) { + return updateArrayBindingPattern(node, elements); + } + const pattern = createArrayBindingPattern(elements, location); + return node ? setOriginalNode(pattern, node) : pattern; + } + + function createOrUpdateObjectBindingPattern(node: Node, elements: BindingElement[], location: TextRange) { + if (node && node.kind === SyntaxKind.ArrayBindingPattern) { + return updateObjectBindingPattern(node, elements); + } + const pattern = createObjectBindingPattern(elements, location); + return node ? setOriginalNode(pattern, node) : pattern; + } + + /** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement + * `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);`*/ + function createRestCall(value: Expression, elements: EffectiveBindingElement[], location: TextRange): Expression { + const propertyNames: LiteralExpression[] = []; + for (let i = 0; i < elements.length - 1; i++) { + if (isOmittedExpression(elements[i])) { + continue; + } + const str = createSynthesizedNode(SyntaxKind.StringLiteral); + str.pos = location.pos; + str.end = location.end; + str.text = getTextOfPropertyName(getEffectivePropertyNameOfEffectiveBindingElement(elements[i])); + propertyNames.push(str); + } + const args = createSynthesizedNodeArray([value, createArrayLiteral(propertyNames, location)]); + return createCall(createIdentifier("__rest"), undefined, args); + } } diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 1fad209bdb3..9013fabf584 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -105,7 +105,7 @@ namespace ts { * @param node A BinaryExpression node. */ function visitBinaryExpression(node: BinaryExpression): Expression { - if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.AssertESNext) { + if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsESNext) { return flattenDestructuringAssignment(context, node, /*needsDestructuringValue*/ true, hoistVariableDeclaration, visitor, /*transformRest*/ true); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c814a2b1d46..e8018083783 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1175,16 +1175,18 @@ namespace ts { right: Expression; } - export interface AssignmentExpression extends BinaryExpression { + export type AssignmentOperatorToken = Token; + + export interface AssignmentExpression extends BinaryExpression { left: LeftHandSideExpression; - operatorToken: Token; + operatorToken: TOperator; } - export interface ObjectDestructuringAssignment extends AssignmentExpression { + export interface ObjectDestructuringAssignment extends AssignmentExpression { left: ObjectLiteralExpression; } - export interface ArrayDestructuringAssignment extends AssignmentExpression { + export interface ArrayDestructuringAssignment extends AssignmentExpression { left: ArrayLiteralExpression; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index c49c33b857f..f0a666e1531 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3125,19 +3125,21 @@ namespace ts { } } - export function isAssignmentExpression(node: Node): node is AssignmentExpression { + export function isAssignmentExpression(node: Node, excludeCompoundAssignment: true): node is AssignmentExpression; + export function isAssignmentExpression(node: Node, excludeCompoundAssignment?: false): node is AssignmentExpression; + export function isAssignmentExpression(node: Node, excludeCompoundAssignment?: boolean): node is AssignmentExpression { return isBinaryExpression(node) - && isAssignmentOperator(node.operatorToken.kind) + && (excludeCompoundAssignment + ? node.operatorToken.kind === SyntaxKind.EqualsToken + : isAssignmentOperator(node.operatorToken.kind)) && isLeftHandSideExpression(node.left); } export function isDestructuringAssignment(node: Node): node is DestructuringAssignment { - if (isBinaryExpression(node)) { - if (node.operatorToken.kind === SyntaxKind.EqualsToken) { - const kind = node.left.kind; - return kind === SyntaxKind.ObjectLiteralExpression - || kind === SyntaxKind.ArrayLiteralExpression; - } + if (isAssignmentExpression(node, /*excludeCompoundAssignment*/ true)) { + const kind = node.left.kind; + return kind === SyntaxKind.ObjectLiteralExpression + || kind === SyntaxKind.ArrayLiteralExpression; } return false; @@ -3918,6 +3920,14 @@ namespace ts { // Binding patterns + export function isArrayBindingPattern(node: Node): node is ArrayBindingPattern { + return node.kind === SyntaxKind.ArrayBindingPattern; + } + + export function isObjectBindingPattern(node: Node): node is ObjectBindingPattern { + return node.kind === SyntaxKind.ObjectBindingPattern; + } + export function isBindingPattern(node: Node): node is BindingPattern { if (node) { const kind = node.kind; diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index cc27d5c9ddf..ebb3ec01ed3 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -701,6 +701,7 @@ namespace ts { return updateParameter(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), + (node).dotDotDotToken, visitNode((node).name, visitor, isBindingName), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), visitNode((node).initializer, visitor, isExpression, /*optional*/ true)); @@ -767,6 +768,7 @@ namespace ts { case SyntaxKind.BindingElement: return updateBindingElement(node, + (node).dotDotDotToken, visitNode((node).propertyName, visitor, isPropertyName, /*optional*/ true), visitNode((node).name, visitor, isBindingName), visitNode((node).initializer, visitor, isExpression, /*optional*/ true)); @@ -1287,6 +1289,13 @@ namespace ts { ? (node: Node, message?: string) => assert(false, message || "Unexpected node.", () => `Node ${formatSyntaxKind(node.kind)} was unexpected.`) : noop; + export const assertEachNode = shouldAssert(AssertionLevel.Normal) + ? (nodes: Node[], test: (node: Node) => boolean, message?: string) => assert( + test === undefined || every(nodes, test), + message || "Unexpected node.", + () => `Node array did not pass test '${getFunctionName(test)}'.`) + : noop; + export const assertNode = shouldAssert(AssertionLevel.Normal) ? (node: Node, test: (node: Node) => boolean, message?: string) => assert( test === undefined || test(node), @@ -1294,6 +1303,27 @@ namespace ts { () => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`) : noop; + export const assertOptionalNode = shouldAssert(AssertionLevel.Normal) + ? (node: Node, test: (node: Node) => boolean, message?: string) => assert( + test === undefined || node === undefined || test(node), + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`) + : noop; + + export const assertOptionalToken = shouldAssert(AssertionLevel.Normal) + ? (node: Node, kind: SyntaxKind, message?: string) => assert( + kind === undefined || node === undefined || node.kind === kind, + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node.kind)} was not a '${formatSyntaxKind(kind)}' token.`) + : noop; + + export const assertMissingNode = shouldAssert(AssertionLevel.Normal) + ? (node: Node, message?: string) => assert( + node === undefined, + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node.kind)} was unexpected'.`) + : noop; + function getFunctionName(func: Function) { if (typeof func !== "function") { return ""; From d5b9263433c7e6b77eee0b138005333eebc00058 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 14 Nov 2016 10:40:26 -0800 Subject: [PATCH 05/26] Consolidate destructuring code paths --- src/compiler/binder.ts | 114 +- src/compiler/factory.ts | 47 +- src/compiler/transformers/destructuring.ts | 1157 ++++------------- src/compiler/transformers/es2015.ts | 63 +- src/compiler/transformers/es2017.ts | 154 +-- src/compiler/transformers/esnext.ts | 60 +- src/compiler/transformers/jsx.ts | 8 +- src/compiler/transformers/module/module.ts | 12 +- src/compiler/transformers/module/system.ts | 10 +- src/compiler/transformers/ts.ts | 6 +- src/compiler/types.ts | 81 +- src/compiler/visitor.ts | 447 ++++--- .../reference/assignmentTypeNarrowing.js | 10 +- .../reference/asyncMethodWithSuper_es5.js | 8 +- .../computedPropertiesInDestructuring1.js | 16 +- .../reference/declarationsAndAssignments.js | 6 +- .../destructuringAssignmentWithDefault.js | 2 +- .../emptyAssignmentPatterns02_ES5.js | 4 +- .../emptyAssignmentPatterns04_ES5.js | 5 +- .../initializePropertiesWithRenamedLet.js | 5 +- .../reference/missingAndExcessProperties.js | 16 +- tests/baselines/reference/objectRest.js | 4 +- .../reference/objectRestAssignment.js | 6 +- .../baselines/reference/objectRestNegative.js | 4 +- ...thandPropertyAssignmentsInDestructuring.js | 40 +- 25 files changed, 889 insertions(+), 1396 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 3e65b547d5b..9a477f05211 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -570,6 +570,31 @@ namespace ts { } } + function bindEach(nodes: NodeArray) { + if (nodes === undefined) { + return; + } + + if (skipTransformFlagAggregation) { + forEach(nodes, bind); + } + else { + const savedSubtreeTransformFlags = subtreeTransformFlags; + subtreeTransformFlags = TransformFlags.None; + let nodeArrayFlags = TransformFlags.None; + for (const node of nodes) { + bind(node); + nodeArrayFlags |= node.transformFlags & ~TransformFlags.HasComputedFlags; + } + nodes.transformFlags = nodeArrayFlags | TransformFlags.HasComputedFlags; + subtreeTransformFlags |= savedSubtreeTransformFlags; + } + } + + function bindEachChild(node: Node) { + forEachChild(node, bind, bindEach); + } + function bindChildrenWorker(node: Node): void { // Binding of JsDocComment should be done before the current block scope container changes. // because the scope of JsDocComment should not be affected by whether the current node is a @@ -578,7 +603,7 @@ namespace ts { forEach(node.jsDocComments, bind); } if (checkUnreachable(node)) { - forEachChild(node, bind); + bindEachChild(node); return; } switch (node.kind) { @@ -643,7 +668,7 @@ namespace ts { bindCallExpressionFlow(node); break; default: - forEachChild(node, bind); + bindEachChild(node); break; } } @@ -976,7 +1001,7 @@ namespace ts { return undefined; } - function bindbreakOrContinueFlow(node: BreakOrContinueStatement, breakTarget: FlowLabel, continueTarget: FlowLabel) { + function bindBreakOrContinueFlow(node: BreakOrContinueStatement, breakTarget: FlowLabel, continueTarget: FlowLabel) { const flowLabel = node.kind === SyntaxKind.BreakStatement ? breakTarget : continueTarget; if (flowLabel) { addAntecedent(flowLabel, currentFlow); @@ -990,11 +1015,11 @@ namespace ts { const activeLabel = findActiveLabel(node.label.text); if (activeLabel) { activeLabel.referenced = true; - bindbreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget); + bindBreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget); } } else { - bindbreakOrContinueFlow(node, currentBreakTarget, currentContinueTarget); + bindBreakOrContinueFlow(node, currentBreakTarget, currentContinueTarget); } } @@ -1062,6 +1087,8 @@ namespace ts { } function bindCaseBlock(node: CaseBlock): void { + const savedSubtreeTransformFlags = subtreeTransformFlags; + subtreeTransformFlags = 0; const clauses = node.clauses; let fallthroughFlow = unreachableFlow; for (let i = 0; i < clauses.length; i++) { @@ -1081,6 +1108,8 @@ namespace ts { errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); } } + clauses.transformFlags = subtreeTransformFlags | TransformFlags.HasComputedFlags; + subtreeTransformFlags |= savedSubtreeTransformFlags; } function bindCaseClause(node: CaseClause): void { @@ -1088,7 +1117,7 @@ namespace ts { currentFlow = preSwitchCaseFlow; bind(node.expression); currentFlow = saveCurrentFlow; - forEach(node.statements, bind); + bindEach(node.statements); } function pushActiveLabel(name: string, breakTarget: FlowLabel, continueTarget: FlowLabel): ActiveLabel { @@ -1180,12 +1209,12 @@ namespace ts { const saveTrueTarget = currentTrueTarget; currentTrueTarget = currentFalseTarget; currentFalseTarget = saveTrueTarget; - forEachChild(node, bind); + bindEachChild(node); currentFalseTarget = currentTrueTarget; currentTrueTarget = saveTrueTarget; } else { - forEachChild(node, bind); + bindEachChild(node); if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { bindAssignmentTargetFlow(node.operand); } @@ -1193,7 +1222,7 @@ namespace ts { } function bindPostfixUnaryExpressionFlow(node: PostfixUnaryExpression) { - forEachChild(node, bind); + bindEachChild(node); if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { bindAssignmentTargetFlow(node.operand); } @@ -1212,7 +1241,7 @@ namespace ts { } } else { - forEachChild(node, bind); + bindEachChild(node); if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) { bindAssignmentTargetFlow(node.left); if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) { @@ -1226,7 +1255,7 @@ namespace ts { } function bindDeleteExpressionFlow(node: DeleteExpression) { - forEachChild(node, bind); + bindEachChild(node); if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { bindAssignmentTargetFlow(node.expression); } @@ -1261,7 +1290,7 @@ namespace ts { } function bindVariableDeclarationFlow(node: VariableDeclaration) { - forEachChild(node, bind); + bindEachChild(node); if (node.initializer || node.parent.parent.kind === SyntaxKind.ForInStatement || node.parent.parent.kind === SyntaxKind.ForOfStatement) { bindInitializedVariableFlow(node); } @@ -1276,12 +1305,12 @@ namespace ts { expr = (expr).expression; } if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) { - forEach(node.typeArguments, bind); - forEach(node.arguments, bind); + bindEach(node.typeArguments); + bindEach(node.arguments); bind(node.expression); } else { - forEachChild(node, bind); + bindEachChild(node); } if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { const propertyAccess = node.expression; @@ -2514,7 +2543,7 @@ namespace ts { transformFlags |= TransformFlags.AssertTypeScript; } - if (subtreeFlags & TransformFlags.ContainsSpreadExpression + if (subtreeFlags & TransformFlags.ContainsSpread || isSuperOrSuperProperty(expression, expressionKind)) { // If the this node contains a SpreadExpression, or is a super call, then it is an ES6 // node. @@ -2545,7 +2574,7 @@ namespace ts { if (node.typeArguments) { transformFlags |= TransformFlags.AssertTypeScript; } - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & TransformFlags.ContainsSpread) { // If the this node contains a SpreadElementExpression then it is an ES6 // node. transformFlags |= TransformFlags.AssertES2015; @@ -2554,7 +2583,6 @@ namespace ts { return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes; } - function computeBinaryExpression(node: BinaryExpression, subtreeFlags: TransformFlags) { let transformFlags = subtreeFlags; const operatorTokenKind = node.operatorToken.kind; @@ -2601,7 +2629,7 @@ namespace ts { } // parameters with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { transformFlags |= TransformFlags.AssertESNext; } @@ -2723,7 +2751,7 @@ namespace ts { } node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.NodeExcludes; + return transformFlags & ~TransformFlags.CatchClauseExcludes; } function computeExpressionWithTypeArguments(node: ExpressionWithTypeArguments, subtreeFlags: TransformFlags) { @@ -2839,7 +2867,7 @@ namespace ts { } // function declarations with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { transformFlags |= TransformFlags.AssertESNext; } @@ -2881,7 +2909,7 @@ namespace ts { } // function expressions with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { transformFlags |= TransformFlags.AssertESNext; } @@ -2924,7 +2952,7 @@ namespace ts { } // arrow functions with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { transformFlags |= TransformFlags.AssertESNext; } @@ -3178,16 +3206,19 @@ namespace ts { break; case SyntaxKind.SpreadElement: + transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsSpread; + break; + case SyntaxKind.SpreadAssignment: - // This node is ES6 or ES next syntax, but is handled by a containing node. - transformFlags |= TransformFlags.ContainsSpreadExpression; + transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsSpread | TransformFlags.ContainsObjectSpread; break; case SyntaxKind.BindingElement: - if ((node as BindingElement).dotDotDotToken) { - // this node is ES2015 or ES next syntax, but is handled by a containing node. - transformFlags |= TransformFlags.ContainsSpreadExpression; + transformFlags |= TransformFlags.AssertES2015; + if ((node).dotDotDotToken) { + transformFlags |= TransformFlags.ContainsRest; } + break; case SyntaxKind.SuperKeyword: // This node is ES6 syntax. @@ -3200,14 +3231,16 @@ namespace ts { break; case SyntaxKind.ObjectBindingPattern: + transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; + if (subtreeFlags & TransformFlags.ContainsSpread) { + transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectRest; + } + excludeFlags = TransformFlags.BindingPatternExcludes; + break; + case SyntaxKind.ArrayBindingPattern: - // These nodes are ES2015 or ES Next syntax. - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { - transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsBindingPattern; - } - else { - transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; - } + transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; + excludeFlags = TransformFlags.BindingPatternExcludes; break; case SyntaxKind.Decorator: @@ -3229,10 +3262,10 @@ namespace ts { transformFlags |= TransformFlags.ContainsLexicalThis; } - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & TransformFlags.ContainsSpread) { // If an ObjectLiteralExpression contains a spread element, then it // is an ES next node. - transformFlags |= TransformFlags.AssertESNext; + transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectSpread; } break; @@ -3240,7 +3273,7 @@ namespace ts { case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.NewExpression: excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes; - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & TransformFlags.ContainsSpread) { // If the this node contains a SpreadExpression, then it is an ES6 // node. transformFlags |= TransformFlags.AssertES2015; @@ -3333,6 +3366,11 @@ namespace ts { return TransformFlags.TypeExcludes; case SyntaxKind.ObjectLiteralExpression: return TransformFlags.ObjectLiteralExcludes; + case SyntaxKind.CatchClause: + return TransformFlags.CatchClauseExcludes; + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + return TransformFlags.BindingPatternExcludes; default: return TransformFlags.NodeExcludes; } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 6674ffc7263..ac3d5ed6e29 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2341,6 +2341,13 @@ namespace ts { // Utilities + export function convertToFunctionBody(node: ConciseBody) { + if (isBlock(node)) { + return node; + } + return createBlock([createReturn(node, node)], node); + } + function isUseStrictPrologue(node: ExpressionStatement): boolean { return (node.expression as StringLiteral).text === "use strict"; } @@ -3121,7 +3128,7 @@ namespace ts { } addCaptureThisForNodeIfNeeded(statements, node, enableSubstitutionsForCapturedThis); - addDefaultValueAssignmentsIfNeeded(statements, node, visitor, convertObjectRest); + addDefaultValueAssignmentsIfNeeded(context, statements, node, visitor, convertObjectRest); addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false); // If we added any generated statements, this must be a multi-line block. @@ -3236,7 +3243,8 @@ namespace ts { * @param statements The statements for the new function body. * @param node A function-like node. */ - export function addDefaultValueAssignmentsIfNeeded(statements: Statement[], + export function addDefaultValueAssignmentsIfNeeded(context: TransformationContext, + statements: Statement[], node: FunctionLikeDeclaration, visitor: (node: Node) => VisitResult, convertObjectRest: boolean): void { @@ -3254,7 +3262,7 @@ namespace ts { } if (isBindingPattern(name)) { - addDefaultValueAssignmentForBindingPattern(statements, parameter, name, initializer, visitor, convertObjectRest); + addDefaultValueAssignmentForBindingPattern(context, statements, parameter, name, initializer, visitor, convertObjectRest); } else if (initializer) { addDefaultValueAssignmentForInitializer(statements, parameter, name, initializer, visitor); @@ -3270,7 +3278,8 @@ namespace ts { * @param name The name of the parameter. * @param initializer The initializer for the parameter. */ - function addDefaultValueAssignmentForBindingPattern(statements: Statement[], + function addDefaultValueAssignmentForBindingPattern(context: TransformationContext, + statements: Statement[], parameter: ParameterDeclaration, name: BindingPattern, initializer: Expression, visitor: (node: Node) => VisitResult, @@ -3286,7 +3295,17 @@ namespace ts { createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList( - flattenParameterDestructuring(parameter, temp, visitor, convertObjectRest) + flattenDestructuringToDeclarations( + context, + parameter, + temp, + /*skipInitializer*/ convertObjectRest, + /*recordTempVariablesInLine*/ true, + convertObjectRest + ? FlattenLevel.ObjectRest + : FlattenLevel.All, + visitor + ) ) ), EmitFlags.CustomPrologue @@ -3490,12 +3509,16 @@ namespace ts { if (firstOriginalDeclaration && isBindingPattern(firstOriginalDeclaration.name)) { // This works whether the declaration is a var, let, or const. // It will use rhsIterationValue _a[_i] as the initializer. - const declarations = flattenVariableDestructuring( + const declarations = flattenDestructuringToDeclarations( + context, firstOriginalDeclaration, elementAccess, - visitor, - /*recordTempVariable*/ undefined, + /*skipInitializer*/ false, + /*recordTempVariablesInLine*/ true, convertObjectRest + ? FlattenLevel.ObjectRest + : FlattenLevel.All, + visitor ); const declarationList = createVariableDeclarationList(declarations, /*location*/ initializer); @@ -3543,13 +3566,15 @@ namespace ts { // This is a destructuring pattern, so we flatten the destructuring instead. statements.push( createStatement( - flattenDestructuringAssignment( + flattenDestructuringToExpression( context, assignment, /*needsValue*/ false, - context.hoistVariableDeclaration, - visitor, convertObjectRest + ? FlattenLevel.ObjectRest + : FlattenLevel.All, + /*createAssignmentCallback*/ undefined, + visitor ) ) ); diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index b0cb3f9d239..d4886457062 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -3,685 +3,218 @@ /*@internal*/ namespace ts { - type EffectiveBindingElement = VariableDeclaration | ParameterDeclaration | BindingElement | ObjectLiteralElementLike | Expression; - type EffectiveObjectBindingPattern = ObjectBindingPattern | ObjectLiteralExpression; - type EffectiveArrayBindingPattern = ArrayBindingPattern | ArrayLiteralExpression; - type EffectiveBindingPattern = EffectiveObjectBindingPattern | EffectiveArrayBindingPattern; - type EffectiveBindingTarget = EffectiveBindingPattern | Expression; - type EffectiveRestIndicator = DotDotDotToken | SpreadElement | SpreadAssignment; - - export const enum FlattenLevel { - All, - ObjectRestDestructuringOnly, - } - - /** - * 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. - * @param visitor An optional visitor to use to visit expressions. - */ - export function flattenDestructuringAssignment( - context: TransformationContext, - node: BinaryExpression, - needsValue: boolean, - recordTempVariable: (node: Identifier) => void, - visitor?: (node: Node) => VisitResult, - transformRest?: boolean): Expression { - - if (isEmptyObjectLiteralOrArrayLiteral(node.left)) { - const right = node.right; - if (isDestructuringAssignment(right)) { - return flattenDestructuringAssignment(context, right, needsValue, recordTempVariable, visitor); - } - else { - return node.right; - } - } - - let location: TextRange = node; - let value = node.right; - const expressions: Expression[] = []; - if (needsValue) { - // If the right-hand value of the destructuring assignment needs to be preserved (as - // is the case when the destructuring assignmen) is part of a larger expression), - // then we need to cache the right-hand value. - // - // The source map location for the assignment should point to the entire binary - // expression. - value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment, visitor); - } - else if (nodeIsSynthesized(node)) { - // Generally, the source map location for a destructuring assignment is the root - // expression. - // - // However, if the root expression is synthesized (as in the case - // of the initializer when transforming a ForOfStatement), then the source map - // location should point to the right-hand value of the expression. - location = value; - } - - flattenDestructuringOld(node, value, location, emitAssignment, emitTempVariableAssignment, recordTempVariable, emitRestAssignment, transformRest, visitor); - - if (needsValue) { - expressions.push(value); - } - - const expression = inlineExpressions(expressions); - aggregateTransformFlags(expression); - return expression; - - function emitAssignment(name: Identifier | ObjectLiteralExpression, value: Expression, location: TextRange) { - const expression = createAssignment(name, value, location); - - // NOTE: this completely disables source maps, but aligns with the behavior of - // `emitAssignment` in the old emitter. - setEmitFlags(expression, EmitFlags.NoNestedSourceMaps); - - aggregateTransformFlags(expression); - expressions.push(expression); - } - - function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(recordTempVariable); - emitAssignment(name, value, location); - return name; - } - - function emitRestAssignment(elements: ObjectLiteralElementLike[], value: Expression, location: TextRange) { - emitAssignment(createObjectLiteral(elements), value, location); - } - } - - /** - * Flattens binding patterns in a parameter declaration. - * - * @param node The ParameterDeclaration to flatten. - * @param value The rhs value for the binding pattern. - * @param visitor An optional visitor to use to visit expressions. - */ - export function flattenParameterDestructuring( - node: ParameterDeclaration, - value: Expression, - visitor?: (node: Node) => VisitResult, - transformRest?: boolean) { - const declarations: VariableDeclaration[] = []; - - flattenDestructuringOld(node, value, node, emitAssignment, emitTempVariableAssignment, noop, emitRestAssignment, transformRest, visitor); - - return declarations; - - function emitAssignment(name: Identifier | BindingPattern, value: Expression, location: TextRange) { - const declaration = createVariableDeclaration(name, /*type*/ undefined, value, location); - - // NOTE: this completely disables source maps, but aligns with the behavior of - // `emitAssignment` in the old emitter. - setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps); - - aggregateTransformFlags(declaration); - declarations.push(declaration); - } - - function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(/*recordTempVariable*/ undefined); - emitAssignment(name, value, location); - return name; - } - - function emitRestAssignment(elements: BindingElement[], value: Expression, location: TextRange) { - emitAssignment(createObjectBindingPattern(elements), value, location); - } - } - - /** - * Flattens binding patterns in a variable declaration. - * - * @param node The VariableDeclaration to flatten. - * @param value An optional rhs value for the binding pattern. - * @param visitor An optional visitor to use to visit expressions. - */ - export function flattenVariableDestructuring( - node: VariableDeclaration, - value?: Expression, - visitor?: (node: Node) => VisitResult, - recordTempVariable?: (node: Identifier) => void, - transformRest?: boolean) { - const declarations: VariableDeclaration[] = []; - - let pendingAssignments: Expression[]; - flattenDestructuringOld(node, value, node, emitAssignment, emitTempVariableAssignment, recordTempVariable, emitRestAssignment, transformRest, visitor); - - return declarations; - - function emitAssignment(name: Identifier | BindingPattern, value: Expression, location: TextRange, original: Node) { - if (pendingAssignments) { - pendingAssignments.push(value); - value = inlineExpressions(pendingAssignments); - pendingAssignments = undefined; - } - - const declaration = createVariableDeclaration(name, /*type*/ undefined, value, location); - declaration.original = original; - - // NOTE: this completely disables source maps, but aligns with the behavior of - // `emitAssignment` in the old emitter. - setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps); - - declarations.push(declaration); - aggregateTransformFlags(declaration); - } - - function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(recordTempVariable); - if (recordTempVariable) { - const assignment = createAssignment(name, value, location); - if (pendingAssignments) { - pendingAssignments.push(assignment); - } - else { - pendingAssignments = [assignment]; - } - } - else { - emitAssignment(name, value, location, /*original*/ undefined); - } - return name; - } - - function emitRestAssignment(elements: BindingElement[], value: Expression, location: TextRange, original: Node) { - emitAssignment(createObjectBindingPattern(elements), value, location, original); - } - } - - /** - * 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. - * @param createAssignmentCallback An optional callback used to create assignment expressions - * for non-temporary variables. - * @param visitor An optional visitor to use to visit expressions. - */ - export function flattenVariableDestructuringToExpression( - node: VariableDeclaration, - recordTempVariable: (name: Identifier) => void, - createAssignmentCallback?: (name: Identifier, value: Expression, location?: TextRange) => Expression, - visitor?: (node: Node) => VisitResult) { - - const pendingAssignments: Expression[] = []; - - flattenDestructuringOld(node, /*value*/ undefined, node, emitAssignment, emitTempVariableAssignment, noop, emitRestAssignment, /*transformRest*/ false, visitor); - - const expression = inlineExpressions(pendingAssignments); - aggregateTransformFlags(expression); - return expression; - - function emitAssignment(name: Identifier | ObjectLiteralExpression, value: Expression, location: TextRange, original: Node) { - const expression = createAssignmentCallback - ? createAssignmentCallback(name.kind === SyntaxKind.Identifier ? name : emitTempVariableAssignment(name, location), - value, - location) - : createAssignment(name, value, location); - - emitPendingAssignment(expression, original); - } - - function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(recordTempVariable); - emitPendingAssignment(createAssignment(name, value, location), /*original*/ undefined); - return name; - } - - function emitRestAssignment(elements: ObjectLiteralElementLike[], value: Expression, location: TextRange, original: Node) { - emitAssignment(createObjectLiteral(elements), value, location, original); - } - - function emitPendingAssignment(expression: Expression, original: Node) { - expression.original = original; - - // NOTE: this completely disables source maps, but aligns with the behavior of - // `emitAssignment` in the old emitter. - setEmitFlags(expression, EmitFlags.NoNestedSourceMaps); - - pendingAssignments.push(expression); - } - } - - function flattenDestructuringOld( - root: VariableDeclaration | ParameterDeclaration | BindingElement | BinaryExpression, - value: Expression, - location: TextRange, - emitAssignment: (name: Identifier, value: Expression, location: TextRange, original: Node) => void, - emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier, - recordTempVariable: (node: Identifier) => void, - emitRestAssignment: (elements: (ObjectLiteralElementLike[] | BindingElement[]), value: Expression, location: TextRange, original: Node) => void, - transformRest: boolean, - visitor?: (node: Node) => VisitResult) { - if (value && visitor) { - value = visitNode(value, visitor, isExpression); - } - - 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)) { - const initializer = visitor - ? visitNode(bindingTarget.objectAssignmentInitializer, visitor, isExpression) - : bindingTarget.objectAssignmentInitializer; - - if (initializer) { - value = createDefaultValueCheck(value, initializer, location); - } - - target = bindingTarget.name; - } - else if (isBinaryExpression(bindingTarget) && bindingTarget.operatorToken.kind === SyntaxKind.EqualsToken) { - const initializer = visitor - ? visitNode(bindingTarget.right, visitor, isExpression) - : bindingTarget.right; - - value = createDefaultValueCheck(value, initializer, 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 = getMutableClone(target); - setSourceMapRange(name, target); - setCommentRange(name, 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 = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); - } - - let bindingElements: ObjectLiteralElementLike[] = []; - for (let i = 0; i < properties.length; i++) { - const p = properties[i]; - if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) { - if (!transformRest || - p.transformFlags & TransformFlags.ContainsSpreadExpression || - (p.kind === SyntaxKind.PropertyAssignment && p.initializer.transformFlags & TransformFlags.ContainsSpreadExpression)) { - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, location, target); - bindingElements = []; - } - const propName = (p).name; - const bindingTarget = p.kind === SyntaxKind.ShorthandPropertyAssignment ? p : (p).initializer || propName; - // Assignment for bindingTarget = value.propName should highlight whole property, hence use p as source map node - emitDestructuringAssignment(bindingTarget, createDestructuringPropertyAccess(value, propName), p); - } - else { - bindingElements.push(p); - } - } - else if (i === properties.length - 1 && p.kind === SyntaxKind.SpreadAssignment) { - Debug.assert((p as SpreadAssignment).expression.kind === SyntaxKind.Identifier); - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, location, target); - bindingElements = []; - } - const propName = (p as SpreadAssignment).expression as Identifier; - const restCall = createRestCall(value, target.properties, p => p.name, target); - emitDestructuringAssignment(propName, restCall, p); - } - } - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, location, target); - bindingElements = []; - } - } - - function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) { - if (transformRest) { - emitESNextArrayLiteralAssignment(target, value, location); - } - else { - emitES2015ArrayLiteralAssignment(target, value, location); - } - } - - function emitESNextArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) { - const elements = target.elements; - const numElements = elements.length; - if (numElements !== 1) { - // For anything but a single element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. - // When doing so we want to highlight the passed-in source map node since thats the one needing this temp assignment - value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); - } - - const expressions: Expression[] = []; - const spreadContainingExpressions: [Expression, Identifier][] = []; - for (let i = 0; i < numElements; i++) { - const e = elements[i]; - if (e.kind === SyntaxKind.OmittedExpression) { - continue; - } - if (e.transformFlags & TransformFlags.ContainsSpreadExpression && i < numElements - 1) { - const tmp = createTempVariable(recordTempVariable); - spreadContainingExpressions.push([e, tmp]); - expressions.push(tmp); - } - else { - expressions.push(e); - } - } - emitAssignment(updateArrayLiteral(target, expressions) as any as Identifier, value, undefined, undefined); - for (const [e, tmp] of spreadContainingExpressions) { - emitDestructuringAssignment(e, tmp, e); - } - } - - function emitES2015ArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) { - const elements = target.elements; - const numElements = elements.length; - if (numElements !== 1) { - // For anything but a single element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. - // When doing so we want to highlight the passed-in source map node since thats the one needing this temp assignment - value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); - } - - for (let i = 0; i < numElements; i++) { - const e = elements[i]; - if (e.kind !== SyntaxKind.OmittedExpression) { - // Assignment for target = value.propName should highligh whole property, hence use e as source map node - if (e.kind !== SyntaxKind.SpreadElement) { - emitDestructuringAssignment(e, createElementAccess(value, createLiteral(i)), e); - } - else if (i === numElements - 1) { - emitDestructuringAssignment((e).expression, createArraySlice(value, i), e); - } - } - } - } - - /** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement - * `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);`*/ - function createRestCall(value: Expression, elements: T[], getPropertyName: (element: T) => PropertyName, location: TextRange): Expression { - const propertyNames: LiteralExpression[] = []; - for (let i = 0; i < elements.length - 1; i++) { - if (isOmittedExpression(elements[i])) { - continue; - } - const str = createSynthesizedNode(SyntaxKind.StringLiteral); - str.pos = location.pos; - str.end = location.end; - str.text = getTextOfPropertyName(getPropertyName(elements[i])); - propertyNames.push(str); - } - const args = createSynthesizedNodeArray([value, createArrayLiteral(propertyNames, location)]); - return createCall(createIdentifier("__rest"), undefined, args); - } - - function emitBindingElement(target: VariableDeclaration | ParameterDeclaration | BindingElement, value: Expression) { - // Any temporary assignments needed to emit target = value should point to target - const initializer = visitor ? visitNode(target.initializer, visitor, isExpression) : target.initializer; - if (transformRest) { - value = value || initializer; - } - else if (initializer) { - // Combine value and initializer - value = value ? createDefaultValueCheck(value, initializer, target) : initializer; - } - else if (!value) { - // Use 'void 0' in absence of value and initializer - value = createVoidZero(); - } - - const name = target.name; - if (!isBindingPattern(name)) { - emitAssignment(name, value, target, target); - } - else { - const numElements = name.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 = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ numElements !== 0, target, emitTempVariableAssignment); - } - if (name.kind === SyntaxKind.ArrayBindingPattern) { - emitArrayBindingElement(name as ArrayBindingPattern, value); - } - else { - emitObjectBindingElement(target, value); - } - } - } - - function emitArrayBindingElement(name: ArrayBindingPattern, value: Expression) { - if (transformRest) { - emitESNextArrayBindingElement(name, value); - } - else { - emitES2015ArrayBindingElement(name, value); - } - } - - function emitES2015ArrayBindingElement(name: ArrayBindingPattern, value: Expression) { - const elements = name.elements; - const numElements = elements.length; - for (let i = 0; i < numElements; i++) { - const element = elements[i]; - if (isOmittedExpression(element)) { - continue; - } - if (!element.dotDotDotToken) { - // Rewrite element to a declaration that accesses array element at index i - emitBindingElement(element, createElementAccess(value, i)); - } - else if (i === numElements - 1) { - emitBindingElement(element, createArraySlice(value, i)); - } - } - } - - function emitESNextArrayBindingElement(name: ArrayBindingPattern, value: Expression) { - const elements = name.elements; - const numElements = elements.length; - const bindingElements: BindingElement[] = []; - const spreadContainingElements: BindingElement[] = []; - for (let i = 0; i < numElements; i++) { - const element = elements[i]; - if (isOmittedExpression(element)) { - continue; - } - if (element.transformFlags & TransformFlags.ContainsSpreadExpression && i < numElements - 1) { - spreadContainingElements.push(element); - bindingElements.push(createBindingElement(undefined, undefined, getGeneratedNameForNode(element), undefined, value)); - } - else { - bindingElements.push(element); - } - } - emitAssignment(updateArrayBindingPattern(name, bindingElements) as any as Identifier, value, undefined, undefined); - for (const element of spreadContainingElements) { - emitBindingElement(element, getGeneratedNameForNode(element)); - } - } - - function emitObjectBindingElement(target: VariableDeclaration | ParameterDeclaration | BindingElement, value: Expression) { - const name = target.name as BindingPattern; - const elements = name.elements; - const numElements = elements.length; - let bindingElements: BindingElement[] = []; - for (let i = 0; i < numElements; i++) { - const element = elements[i]; - if (isOmittedExpression(element)) { - continue; - } - if (i === numElements - 1 && element.dotDotDotToken) { - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, target, target); - bindingElements = []; - } - const restCall = createRestCall(value, - name.elements, - element => (element as BindingElement).propertyName || (element as BindingElement).name, - name); - emitBindingElement(element, restCall); - } - else if (transformRest && !(element.transformFlags & TransformFlags.ContainsSpreadExpression)) { - // do not emit until we have a complete bundle of ES2015 syntax - bindingElements.push(element); - } - else { - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, target, target); - bindingElements = []; - } - // Rewrite element to a declaration with an initializer that fetches property - const propName = element.propertyName || element.name; - emitBindingElement(element, createDestructuringPropertyAccess(value, propName)); - } - } - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, target, target); - bindingElements = []; - } - } - - function createDefaultValueCheck(value: Expression, defaultValue: Expression, location: TextRange): Expression { - value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); - return createConditional( - createStrictEquality(value, createVoidZero()), - createToken(SyntaxKind.QuestionToken), - defaultValue, - createToken(SyntaxKind.ColonToken), - value - ); - } - - /** - * Creates either a PropertyAccessExpression or an ElementAccessExpression for the - * right-hand side of a transformed destructuring assignment. - * - * @param expression The right-hand expression that is the source of the property. - * @param propertyName The destructuring property name. - */ - function createDestructuringPropertyAccess(expression: Expression, propertyName: PropertyName): LeftHandSideExpression { - if (isComputedPropertyName(propertyName)) { - return createElementAccess( - expression, - ensureIdentifierOld(propertyName.expression, /*reuseIdentifierExpressions*/ false, /*location*/ propertyName, emitTempVariableAssignment) - ); - } - else if (isLiteralExpression(propertyName)) { - const clone = getSynthesizedClone(propertyName); - clone.text = unescapeIdentifier(clone.text); - return createElementAccess(expression, clone); - } - else { - if (isGeneratedIdentifier(propertyName)) { - const clone = getSynthesizedClone(propertyName); - clone.text = unescapeIdentifier(clone.text); - return createPropertyAccess(expression, clone); - } - else { - return createPropertyAccess(expression, createIdentifier(unescapeIdentifier(propertyName.text))); - } - } - } - } - - /** - * 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. - * @param visitor An optional callback used to visit the value. - */ - function ensureIdentifierOld( - value: Expression, - reuseIdentifierExpressions: boolean, - location: TextRange, - emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier, - visitor?: (node: Node) => VisitResult) { - - if (isIdentifier(value) && reuseIdentifierExpressions) { - return value; - } - else { - if (visitor) { - value = visitNode(value, visitor, isExpression); - } - - return emitTempVariableAssignment(value, location); - } - } + type EffectiveBindingOrAssignmentElement = VariableDeclaration | ParameterDeclaration | BindingElement | ObjectLiteralElementLike | Expression; + type EffectiveObjectBindingOrAssignmentPattern = ObjectBindingPattern | ObjectLiteralExpression; + type EffectiveArrayBindingOrAssignmentPattern = ArrayBindingPattern | ArrayLiteralExpression; + type EffectiveBindingOrAssignmentPattern = EffectiveObjectBindingOrAssignmentPattern | EffectiveArrayBindingOrAssignmentPattern; + type EffectiveBindingOrAssignmentTarget = EffectiveBindingOrAssignmentPattern | Expression; + type EffectiveBindingOrAssignmentRestIndicator = DotDotDotToken | SpreadElement | SpreadAssignment; interface FlattenHost { context: TransformationContext; level: FlattenLevel; recordTempVariablesInLine: boolean; - emitAssignment: (target: EffectiveBindingTarget, value: Expression, location: TextRange, original: Node) => void; - emitArrayAssignment: (elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) => void; - emitObjectAssignment: (elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) => void; emitExpression: (value: Expression) => void; + emitBindingOrAssignment: (target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) => void; + createArrayBindingOrAssignmentPattern: (elements: EffectiveBindingOrAssignmentElement[]) => EffectiveArrayBindingOrAssignmentPattern; + createObjectBindingOrAssignmentPattern: (elements: EffectiveBindingOrAssignmentElement[]) => EffectiveObjectBindingOrAssignmentPattern; + createArrayBindingOrAssignmentElement: (node: Identifier) => EffectiveBindingOrAssignmentElement; + visitor?: (node: Node) => VisitResult; } + export const enum FlattenLevel { + ObjectRest, + All, + } + + export const enum FlattenOutput { + Expression, + Declarations + } + + export const enum FlattenOptions { + NeedsValue = 1 << 0, + SkipInitializer = 1 << 1, + RecordTempVariablesInLine = 1 << 2, + } + + export function flattenDestructuring( + output: FlattenOutput.Expression, + level: FlattenLevel, + node: VariableDeclaration | DestructuringAssignment, + visitor: (node: Node) => VisitResult, + context: TransformationContext, + options: FlattenOptions, + createAssignment?: (name: Identifier, value: Expression, location?: TextRange) => Expression): Expression; + export function flattenDestructuring( + output: FlattenOutput.Declarations, + level: FlattenLevel, + node: VariableDeclaration | ParameterDeclaration, + visitor: (node: Node) => VisitResult, + context: TransformationContext, + options: FlattenOptions, + boundValue?: Expression): VariableDeclaration[]; + export function flattenDestructuring( + _output: FlattenOutput, + _level: FlattenLevel, + _node: VariableDeclaration | ParameterDeclaration | DestructuringAssignment, + _visitor: (node: Node) => VisitResult, + _context: TransformationContext, + _options: FlattenOptions, + _valueOrCallback?: Expression | ((name: Identifier, value: Expression, location?: TextRange) => Expression)): Expression | VariableDeclaration[] { + // const flattenMode = flags & FlattenFlags.FlattenMask; + // const outputMode = flags & FlattenFlags.OutputMask; + // const options = flags & FlattenFlags.OptionsMask; + + + // if (outputMode === FlattenFlags.OutputExpressions) { + // return flattenDestructuringToExpression(context, node, !(options & FlattenFlags.NeedsValue), flattenMode, createAssignmentCallback, visitor); + // } + // else { + // return flattenDestructuringToDeclarations( + // context, + // node, + // boundValue) + // } + return; + } + + /** + * Flattens a DestructuringAssignment or a VariableDeclaration to an expression. + * + * @param context The transformation context + * @param node The node to flatten. + * @param needsValue Indicates whether the value from the right-hand-side of the + * destructuring assignment is needed as part of a larger expression. + * @param level Indicates the extent to which flattening should occur. + * @param createAssignmentCallback A callback used to create the assignment expression. + * @param visitor A visitor used to visit initializers. + */ + export function flattenDestructuringToExpression( + context: TransformationContext, + node: VariableDeclaration | DestructuringAssignment, + needsValue: boolean, + level: FlattenLevel, + createAssignmentCallback: (name: Identifier, value: Expression, location?: TextRange) => Expression, + visitor: (node: Node) => VisitResult): Expression { + + let location: TextRange = node; + let value: Expression; + if (isDestructuringAssignment(node)) { + value = node.right; + while (isEmptyObjectLiteralOrArrayLiteral(node.left)) { + if (isDestructuringAssignment(value)) { + location = node = value; + value = node.right; + } + else { + return value; + } + } + } + + const expressions: Expression[] = []; + const host: FlattenHost = { + context, + level, + recordTempVariablesInLine: false, + emitExpression, + emitBindingOrAssignment, + createArrayBindingOrAssignmentPattern: createEffectiveArrayAssignmentPattern, + createObjectBindingOrAssignmentPattern: createEffectiveObjectAssignmentPattern, + createArrayBindingOrAssignmentElement: createEffectiveAssignmentElement, + visitor + }; + + if (value) { + value = visitNode(value, visitor, isExpression); + if (needsValue) { + // If the right-hand value of the destructuring assignment needs to be preserved (as + // is the case when the destructuring assignment is part of a larger expression), + // then we need to cache the right-hand value. + // + // The source map location for the assignment should point to the entire binary + // expression. + value = ensureIdentifier(host, value, /*reuseIdentifierExpressions*/ true, location); + } + else if (nodeIsSynthesized(node)) { + // Generally, the source map location for a destructuring assignment is the root + // expression. + // + // However, if the root expression is synthesized (as in the case + // of the initializer when transforming a ForOfStatement), then the source map + // location should point to the right-hand value of the expression. + location = value; + } + } + + flattenEffectiveBindingElement(host, node, value, location, /*skipInitializer*/ isDestructuringAssignment(node)); + + if (value && needsValue) { + expressions.push(value); + } + + return aggregateTransformFlags(inlineExpressions(expressions)); + + function emitExpression(expression: Expression) { + // NOTE: this completely disables source maps, but aligns with the behavior of + // `emitAssignment` in the old emitter. + setEmitFlags(expression, EmitFlags.NoNestedSourceMaps); + aggregateTransformFlags(expression); + expressions.push(expression); + } + + function emitBindingOrAssignment(target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) { + Debug.assertNode(target, createAssignmentCallback ? isIdentifier : isExpression); + const expression = createAssignmentCallback + ? createAssignmentCallback(target, value, location) + : createAssignment(visitNode(target, visitor, isExpression), value, location); + expression.original = original; + emitExpression(expression); + } + } + + /** + * Flattens a VariableDeclaration or ParameterDeclaration to one or more variable declarations. + * + * @param context The transformation context + * @param node The node to flatten. + * @param boundValue The value bound to the declaration. + * @param recordTempVariablesInLine Indicates whether temporary variables should be recored in-line. + * @param level Indicates the extent to which flattening should occur. + */ export function flattenDestructuringToDeclarations( context: TransformationContext, node: VariableDeclaration | ParameterDeclaration, boundValue: Expression | undefined, + skipInitializer: boolean, recordTempVariablesInLine: boolean, - level: FlattenLevel): VisitResult { + level: FlattenLevel, + visitor: (node: Node) => VisitResult): VariableDeclaration[] { - const pendingDeclarations: { pendingExpressions?: Expression[], name: BindingName, value: Expression, location?: TextRange, original?: Node; }[] = []; let pendingExpressions: Expression[]; - let declaration: VariableDeclaration; - let declarations: VariableDeclaration[]; + const pendingDeclarations: { pendingExpressions?: Expression[], name: BindingName, value: Expression, location?: TextRange, original?: Node; }[] = []; + const declarations: VariableDeclaration[] = []; const host: FlattenHost = { context, level, recordTempVariablesInLine, emitExpression, - emitAssignment, - emitArrayAssignment, - emitObjectAssignment + emitBindingOrAssignment, + createArrayBindingOrAssignmentPattern: createEffectiveArrayBindingPattern, + createObjectBindingOrAssignmentPattern: createEffectiveObjectBindingPattern, + createArrayBindingOrAssignmentElement: createEffectiveBindingElement, + visitor }; - flattenEffectiveBindingElement(host, node, boundValue, node); + flattenEffectiveBindingElement(host, node, boundValue, node, skipInitializer); if (pendingExpressions) { const temp = createTempVariable(/*recordTempVariable*/ undefined); if (recordTempVariablesInLine) { const value = inlineExpressions(pendingExpressions); pendingExpressions = undefined; - emitAssignment(temp, value, /*location*/ undefined, /*original*/ undefined); + emitBindingOrAssignment(temp, value, /*location*/ undefined, /*original*/ undefined); } else { context.hoistVariableDeclaration(temp); @@ -706,51 +239,33 @@ namespace ts { setEmitFlags(variable, EmitFlags.NoNestedSourceMaps); } aggregateTransformFlags(variable); - if (!declaration) { - declaration = variable; - } - else if (!declarations) { - declarations = [declaration, variable]; - } - else { - declarations.push(variable); - } + declarations.push(variable); } - return declarations || declaration; + return declarations; function emitExpression(value: Expression) { pendingExpressions = append(pendingExpressions, value); } - function emitAssignment(target: EffectiveBindingTarget, value: Expression, location: TextRange, original: Node) { + function emitBindingOrAssignment(target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) { Debug.assertNode(target, isBindingName); - pendingDeclarations.push({ pendingExpressions, name: target, value, location, original }); if (pendingExpressions) { value = inlineExpressions(append(pendingExpressions, value)); pendingExpressions = undefined; } - } - - function emitArrayAssignment(elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) { - Debug.assertEachNode(elements, isArrayBindingElement); - emitAssignment(createArrayBindingPattern(elements), value, location, original); - } - - function emitObjectAssignment(elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) { - Debug.assertEachNode(elements, isBindingElement); - emitAssignment(createObjectBindingPattern(elements), value, location, original); + pendingDeclarations.push({ pendingExpressions, name: target, value, location, original }); } } function flattenEffectiveBindingElement( host: FlattenHost, - bindingElement: EffectiveBindingElement, + bindingElement: EffectiveBindingOrAssignmentElement, boundValue: Expression | undefined, location: TextRange, skipInitializer?: boolean) { if (!skipInitializer) { - const initializer = getInitializerOfEffectiveBindingElement(bindingElement); + const initializer = visitNode(getInitializerOfEffectiveBindingElement(bindingElement), host.visitor, isExpression); if (initializer) { // Combine value and initializer boundValue = boundValue ? createDefaultValueCheck(host, boundValue, initializer, location) : initializer; @@ -762,7 +277,7 @@ namespace ts { } const bindingTarget = getTargetOfEffectiveBindingElement(bindingElement); if (!isEffectiveBindingPattern(bindingTarget)) { - host.emitAssignment(bindingTarget, boundValue, location, /*original*/ bindingElement); + host.emitBindingOrAssignment(bindingTarget, boundValue, location, /*original*/ bindingElement); } else { const elements = getElementsOfEffectiveBindingPattern(bindingTarget); @@ -784,18 +299,20 @@ namespace ts { } } - function flattenEffectiveObjectBindingElements(host: FlattenHost, bindingTarget: EffectiveObjectBindingPattern, elements: EffectiveBindingElement[], boundValue: Expression, location: TextRange) { - let bindingElements: EffectiveBindingElement[]; + function flattenEffectiveObjectBindingElements(host: FlattenHost, bindingTarget: EffectiveObjectBindingOrAssignmentPattern, elements: EffectiveBindingOrAssignmentElement[], boundValue: Expression, location: TextRange) { + let bindingElements: EffectiveBindingOrAssignmentElement[]; const numElements = elements.length; for (let i = 0; i < numElements; i++) { const element = elements[i]; if (!getEffectiveRestIndicator(element)) { - if (host.level >= FlattenLevel.ObjectRestDestructuringOnly && !(element.transformFlags & TransformFlags.ContainsSpreadExpression)) { + if (host.level <= FlattenLevel.ObjectRest + && !(element.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) + && !(getTargetOfEffectiveBindingElement(element).transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest))) { bindingElements = append(bindingElements, element); } else { if (bindingElements) { - host.emitObjectAssignment(bindingElements, boundValue, location, bindingTarget); + host.emitBindingOrAssignment(host.createObjectBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget); bindingElements = undefined; } const propertyName = getEffectivePropertyNameOfEffectiveBindingElement(element); @@ -805,7 +322,7 @@ namespace ts { } else if (i === numElements - 1) { if (bindingElements) { - host.emitObjectAssignment(bindingElements, boundValue, location, bindingTarget); + host.emitBindingOrAssignment(host.createObjectBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget); bindingElements = undefined; } const value = createRestCall(boundValue, elements, bindingTarget); @@ -813,31 +330,29 @@ namespace ts { } } if (bindingElements) { - host.emitObjectAssignment(bindingElements, boundValue, location, bindingTarget); + host.emitBindingOrAssignment(host.createObjectBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget); } } - function flattenEffectiveArrayBindingElements(host: FlattenHost, bindingTarget: EffectiveArrayBindingPattern, elements: EffectiveBindingElement[], boundValue: Expression, location: TextRange) { - let bindingElements: EffectiveBindingElement[]; - let spreadContainingElements: EffectiveBindingElement[]; - let tempNames: Map; + function flattenEffectiveArrayBindingElements(host: FlattenHost, bindingTarget: EffectiveArrayBindingOrAssignmentPattern, elements: EffectiveBindingOrAssignmentElement[], boundValue: Expression, location: TextRange) { + let bindingElements: EffectiveBindingOrAssignmentElement[]; + let spreadContainingElements: [Identifier, EffectiveBindingOrAssignmentElement][]; const numElements = elements.length; for (let i = 0; i < numElements; i++) { const element = elements[i]; - if (isOmittedExpression(element)) { - continue; - } - else if (host.level >= FlattenLevel.ObjectRestDestructuringOnly) { - if (element.transformFlags & TransformFlags.ContainsSpreadExpression) { + if (host.level <= FlattenLevel.ObjectRest) { + if (element.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest) && i < numElements - 1) { const temp = createTempVariable(/*recordTempVariable*/ undefined); - tempNames = appendProperty(tempNames, getNodeId(element), temp); - spreadContainingElements = append(spreadContainingElements, element); - bindingElements = append(bindingElements, temp); + spreadContainingElements = append(spreadContainingElements, <[Identifier, EffectiveBindingOrAssignmentElement]>[temp, element]); + bindingElements = append(bindingElements, host.createArrayBindingOrAssignmentElement(temp)); } else { bindingElements = append(bindingElements, element); } } + else if (isOmittedExpression(element)) { + continue; + } else if (!getEffectiveRestIndicator(element)) { const value = createElementAccess(boundValue, i); flattenEffectiveBindingElement(host, element, value, /*location*/ element); @@ -848,11 +363,11 @@ namespace ts { } } if (bindingElements) { - host.emitArrayAssignment(bindingElements, boundValue, location, bindingTarget); + host.emitBindingOrAssignment(host.createArrayBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget); } if (spreadContainingElements) { - for (const element of spreadContainingElements) { - flattenEffectiveBindingElement(host, element, tempNames[getNodeId(element)], element); + for (const [id, element] of spreadContainingElements) { + flattenEffectiveBindingElement(host, element, id, element); } } } @@ -923,7 +438,7 @@ namespace ts { else { const temp = createTempVariable(/*recordTempVariable*/ undefined); if (host.recordTempVariablesInLine) { - host.emitAssignment(temp, value, location, /*original*/ undefined); + host.emitBindingOrAssignment(temp, value, location, /*original*/ undefined); } else { host.context.hoistVariableDeclaration(temp); @@ -936,7 +451,7 @@ namespace ts { /** * Determines whether the EffectiveBindingElement is a declaration */ - function isDeclarationBindingElement(bindingElement: EffectiveBindingElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement { + function isDeclarationBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement { switch (bindingElement.kind) { case SyntaxKind.VariableDeclaration: case SyntaxKind.Parameter: @@ -950,7 +465,7 @@ namespace ts { /** * Gets the initializer of an EffectiveBindingElement. */ - function getInitializerOfEffectiveBindingElement(bindingElement: EffectiveBindingElement): Expression | undefined { + function getInitializerOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): Expression | undefined { if (isDeclarationBindingElement(bindingElement)) { // `1` in `let { a = 1 } = ...` // `1` in `let { a: b = 1 } = ...` @@ -992,7 +507,7 @@ namespace ts { /** * Gets the name of an EffectiveBindingElement. */ - function getTargetOfEffectiveBindingElement(bindingElement: EffectiveBindingElement): EffectiveBindingTarget { + function getTargetOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): EffectiveBindingOrAssignmentTarget { if (isDeclarationBindingElement(bindingElement)) { // `a` in `let { a } = ...` // `a` in `let { a = 1 } = ...` @@ -1067,7 +582,7 @@ namespace ts { /** * Determines whether an EffectiveBindingElement is a rest element. */ - function getEffectiveRestIndicator(bindingElement: EffectiveBindingElement): EffectiveRestIndicator { + function getEffectiveRestIndicator(bindingElement: EffectiveBindingOrAssignmentElement): EffectiveBindingOrAssignmentRestIndicator { switch (bindingElement.kind) { case SyntaxKind.Parameter: case SyntaxKind.BindingElement: @@ -1086,7 +601,7 @@ namespace ts { /** * Gets the property name of a BindingElement-like element */ - function getEffectivePropertyNameOfEffectiveBindingElement(bindingElement: EffectiveBindingElement) { + function getEffectivePropertyNameOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement) { switch (bindingElement.kind) { case SyntaxKind.BindingElement: // `a` in `let { a: b } = ...` @@ -1126,7 +641,7 @@ namespace ts { /** * Determines whether a node is BindingPattern-like */ - function isEffectiveBindingPattern(node: EffectiveBindingTarget): node is EffectiveBindingPattern { + function isEffectiveBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveBindingOrAssignmentPattern { return isEffectiveObjectBindingPattern(node) || isEffectiveArrayBindingPattern(node); } @@ -1134,7 +649,7 @@ namespace ts { /** * Determines whether a node is ObjectBindingPattern-like */ - function isEffectiveObjectBindingPattern(node: EffectiveBindingTarget): node is EffectiveObjectBindingPattern { + function isEffectiveObjectBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveObjectBindingOrAssignmentPattern { switch (node.kind) { case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ObjectLiteralExpression: @@ -1147,7 +662,7 @@ namespace ts { /** * Determines whether a node is ArrayBindingPattern-like */ - function isEffectiveArrayBindingPattern(node: EffectiveBindingTarget): node is EffectiveArrayBindingPattern { + function isEffectiveArrayBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveArrayBindingOrAssignmentPattern { switch (node.kind) { case SyntaxKind.ArrayBindingPattern: case SyntaxKind.ArrayLiteralExpression: @@ -1160,7 +675,7 @@ namespace ts { /** * Gets the elements of a BindingPattern-like name */ - function getElementsOfEffectiveBindingPattern(name: EffectiveBindingPattern): EffectiveBindingElement[] { + function getElementsOfEffectiveBindingPattern(name: EffectiveBindingOrAssignmentPattern): EffectiveBindingOrAssignmentElement[] { switch (name.kind) { case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: @@ -1175,179 +690,79 @@ namespace ts { } } - // function updateEffectiveBindingElement(bindingElement: EffectiveBindingElement, restIndicator: EffectiveRestIndicator, propertyName: PropertyName, target: EffectiveBindingTarget, initializer: Expression) { - // switch (bindingElement.kind) { - // case SyntaxKind.VariableDeclaration: - // Debug.assertNode(target, isBindingName); - // Debug.assertMissingNode(propertyName); - // return updateVariableDeclaration( - // bindingElement, - // target, - // /*type*/ undefined, - // initializer - // ); - - // case SyntaxKind.Parameter: - // Debug.assertOptionalToken(restIndicator, SyntaxKind.DotDotDotToken); - // Debug.assertMissingNode(propertyName); - // Debug.assertNode(target, isBindingName); - // return updateParameter( - // bindingElement, - // /*decorators*/ undefined, - // /*modifiers*/ undefined, - // restIndicator, - // target, - // /*type*/ undefined, - // initializer - // ); - - // case SyntaxKind.BindingElement: - // Debug.assertOptionalToken(restIndicator, SyntaxKind.DotDotDotToken); - // Debug.assertNode(target, isBindingName); - // return updateBindingElement( - // bindingElement, - // restIndicator, - // propertyName, - // target, - // initializer - // ); - - // case SyntaxKind.PropertyAssignment: - // case SyntaxKind.ShorthandPropertyAssignment: - // case SyntaxKind.SpreadAssignment: - // if (restIndicator) { - // return convertToSpreadAssignment(bindingElement, propertyName, target, initializer); - // } - // else if (propertyName) { - // return convertToPropertyAssignment(bindingElement, propertyName, target, initializer); - // } - // else { - // return convertToShorthandPropertyAssignment(bindingElement, target, initializer); - // } - - // case SyntaxKind.ArrayLiteralExpression: - // case SyntaxKind.BinaryExpression: - // case SyntaxKind.SpreadElement: - // case SyntaxKind.PropertyAccessExpression: - // case SyntaxKind.ElementAccessExpression: - // Debug.assertMissingNode(propertyName); - // if (restIndicator) { - // return convertToSpreadElement(bindingElement, target, initializer); - // } - // else { - - // } - // } - // } - - // function convertToSpreadAssignment(node: PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment, propertyName: PropertyName, target: EffectiveBindingTarget, initializer: Expression) { - // Debug.assertMissingNode(propertyName); - // Debug.assertNode(target, isIdentifier); - // Debug.assertMissingNode(initializer); - // if (node.kind === SyntaxKind.SpreadAssignment) { - // return updateSpreadAssignment(node, target); - // } - // return setOriginalNode( - // createSpreadAssignment( - // target, - // node - // ), - // node - // ); - // } - - // function convertToPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment, propertyName: PropertyName, target: EffectiveBindingTarget, initializer: Expression) { - // Debug.assertNode(target, isExpression); - // if (node.kind === SyntaxKind.PropertyAssignment) { - // return updatePropertyAssignment( - // node, - // propertyName, - // initializer ? - // isAssignmentExpression(node.initializer, /*excludeCompoundAssignment*/ true) - // ? updateBinary(node.initializer, target, initializer) - // : createAssignment(target, initializer) - // : target - // ); - // } - // return setOriginalNode( - // createPropertyAssignment( - // propertyName, - // initializer ? - // createAssignment(target, initializer) - // : target, - // node - // ), - // node - // ); - // } - - // function convertToShorthandPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment, target: EffectiveBindingTarget, initializer: Expression) { - // Debug.assertNode(target, isIdentifier); - // if (node.kind === SyntaxKind.ShorthandPropertyAssignment) { - // return updateShorthandPropertyAssignment( - // node, - // target, - // initializer - // ); - // } - // return setOriginalNode( - // createShorthandPropertyAssignment( - // target, - // initializer, - // node - // ), - // node - // ); - // } - - // function convertToSpreadElement(node: Expression, target: EffectiveBindingTarget, initializer: Expression) { - // Debug.assertNode(target, isExpression); - // Debug.assertMissingNode(initializer); - // if (node.kind === SyntaxKind.SpreadElement) { - // return updateSpread( - // node, - // target); - // } - // return setOriginalNode( - // createSpread( - // target, - // node - // ), - // node - // ); - // } - - // function convertToElement(node: Expression, target: EffectiveBindingTarget, initializer: Expression) { - - // } - - function createOrUpdateVariableDeclaration(node: Node, name: BindingName, initializer: Expression, location: TextRange) { - if (node && node.kind === SyntaxKind.VariableDeclaration) { - return updateVariableDeclaration(node, name, /*type*/ undefined, initializer); - } - const variable = createVariableDeclaration(name, /*type*/ undefined, initializer, location); - return node ? setOriginalNode(variable, node) : variable; + function createEffectiveArrayBindingPattern(elements: EffectiveBindingOrAssignmentElement[]) { + Debug.assertEachNode(elements, isArrayBindingElement); + return createArrayBindingPattern(elements); } - function createOrUpdateArrayBindingPattern(node: Node, elements: ArrayBindingElement[], location: TextRange) { - if (node && node.kind === SyntaxKind.ArrayBindingPattern) { - return updateArrayBindingPattern(node, elements); - } - const pattern = createArrayBindingPattern(elements, location); - return node ? setOriginalNode(pattern, node) : pattern; + function createEffectiveArrayAssignmentPattern(elements: EffectiveBindingOrAssignmentElement[]) { + return createArrayLiteral(map(elements, convertToArrayLiteralElement)); } - function createOrUpdateObjectBindingPattern(node: Node, elements: BindingElement[], location: TextRange) { - if (node && node.kind === SyntaxKind.ArrayBindingPattern) { - return updateObjectBindingPattern(node, elements); + function createEffectiveObjectBindingPattern(elements: EffectiveBindingOrAssignmentElement[]) { + Debug.assertEachNode(elements, isBindingElement); + return createObjectBindingPattern(elements); + } + + function createEffectiveObjectAssignmentPattern(elements: EffectiveBindingOrAssignmentElement[]) { + return createObjectLiteral(map(elements, convertToObjectLiteralElement)); + } + + function createEffectiveBindingElement(name: Identifier) { + return createBindingElement(/*propertyName*/ undefined, /*dotDotDotToken*/ undefined, name); + } + + function createEffectiveAssignmentElement(name: Identifier) { + return name; + } + + function convertToArrayLiteralElement(element: EffectiveBindingOrAssignmentElement) { + if (isBindingElement(element)) { + if (element.dotDotDotToken) { + Debug.assertNode(element.name, isIdentifier); + return setOriginalNode(createSpread(element.name, element), element); + } + const expression = convertToExpressionTarget(element.name); + return element.initializer ? setOriginalNode(createAssignment(expression, element.initializer, element), element) : expression; } - const pattern = createObjectBindingPattern(elements, location); - return node ? setOriginalNode(pattern, node) : pattern; + Debug.assertNode(element, isExpression); + return element; + } + + function convertToObjectLiteralElement(element: EffectiveBindingOrAssignmentElement) { + if (isBindingElement(element)) { + if (element.dotDotDotToken) { + Debug.assertNode(element.name, isIdentifier); + return setOriginalNode(createSpreadAssignment(element.name, element), element); + } + if (element.propertyName) { + const expression = convertToExpressionTarget(element.name); + return setOriginalNode(createPropertyAssignment(element.propertyName, element.initializer ? createAssignment(expression, element.initializer) : expression, element), element); + } + Debug.assertNode(element.name, isIdentifier); + return setOriginalNode(createShorthandPropertyAssignment(element.name, element.initializer, element), element); + } + Debug.assertNode(element, isObjectLiteralElementLike); + return element; + } + + function convertToExpressionTarget(target: EffectiveBindingOrAssignmentTarget): Expression { + if (isBindingPattern(target)) { + switch (target.kind) { + case SyntaxKind.ArrayBindingPattern: + return setOriginalNode(createArrayLiteral(map(target.elements, convertToArrayLiteralElement), target), target); + case SyntaxKind.ObjectBindingPattern: + return setOriginalNode(createObjectLiteral(map(target.elements, convertToObjectLiteralElement), target), target); + } + return; + } + Debug.assertNode(target, isExpression); + return target; } /** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement * `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);`*/ - function createRestCall(value: Expression, elements: EffectiveBindingElement[], location: TextRange): Expression { + function createRestCall(value: Expression, elements: EffectiveBindingOrAssignmentElement[], location: TextRange): Expression { const propertyNames: LiteralExpression[] = []; for (let i = 0; i < elements.length - 1; i++) { if (isOmittedExpression(elements[i])) { diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index e4fb8a0aaf0..dd7498c4149 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -279,7 +279,7 @@ namespace ts { else if (node.transformFlags & TransformFlags.ContainsES2015 || (isInConstructorWithCapturedSuper && !isExpression(node))) { // we want to dive in this branch either if node has children with ES2015 specific syntax // or we are inside constructor that captures result of the super call so all returns without expression should be - // rewritten. Note: we skip expressions since returns should never appear there + // rewritten. Note: we skip expressions since returns should never appear there return visitEachChild(node, visitor, context); } else { @@ -861,7 +861,7 @@ namespace ts { } if (constructor) { - addDefaultValueAssignmentsIfNeeded(statements, constructor, visitor, /*convertObjectRest*/ false); + addDefaultValueAssignmentsIfNeeded(context, statements, constructor, visitor, /*convertObjectRest*/ false); addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper); Debug.assert(statementOffset >= 0, "statementOffset not initialized correctly!"); @@ -1328,16 +1328,10 @@ namespace ts { // If we are here it is most likely because our expression is a destructuring assignment. switch (node.expression.kind) { case SyntaxKind.ParenthesizedExpression: - return updateStatement(node, - visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false) - ); - + return updateStatement(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); case SyntaxKind.BinaryExpression: - return updateStatement(node, - visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false) - ); + return updateStatement(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); } - return visitEachChild(node, visitor, context); } @@ -1350,22 +1344,14 @@ namespace ts { */ function visitParenthesizedExpression(node: ParenthesizedExpression, needsDestructuringValue: boolean): ParenthesizedExpression { // If we are here it is most likely because our expression is a destructuring assignment. - if (needsDestructuringValue) { + if (!needsDestructuringValue) { switch (node.expression.kind) { case SyntaxKind.ParenthesizedExpression: - return createParen( - visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ true), - /*location*/ node - ); - + return updateParen(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); case SyntaxKind.BinaryExpression: - return createParen( - visitBinaryExpression(node.expression, /*needsDestructuringValue*/ true), - /*location*/ node - ); + return updateParen(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); } } - return visitEachChild(node, visitor, context); } @@ -1379,7 +1365,13 @@ namespace ts { function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { // If we are here it is because this is a destructuring assignment. Debug.assert(isDestructuringAssignment(node)); - return flattenDestructuringAssignment(context, node, needsDestructuringValue, hoistVariableDeclaration, visitor); + return flattenDestructuringToExpression( + context, + node, + needsDestructuringValue, + FlattenLevel.All, + /*createAssignmentCallback*/ undefined, + visitor); } function visitVariableStatement(node: VariableStatement): Statement { @@ -1391,7 +1383,14 @@ namespace ts { if (decl.initializer) { let assignment: Expression; if (isBindingPattern(decl.name)) { - assignment = flattenVariableDestructuringToExpression(decl, hoistVariableDeclaration, /*createAssignmentCallback*/ undefined, visitor); + assignment = flattenDestructuringToExpression( + context, + decl, + /*needsValue*/ false, + FlattenLevel.All, + /*createAssignmentCallback*/ undefined, + visitor + ); } else { assignment = createBinary(decl.name, SyntaxKind.EqualsToken, visitNode(decl.initializer, visitor, isExpression)); @@ -1544,8 +1543,16 @@ namespace ts { if (isBindingPattern(node.name)) { const recordTempVariablesInLine = !enclosingVariableStatement || !hasModifier(enclosingVariableStatement, ModifierFlags.Export); - return flattenVariableDestructuring(node, /*value*/ undefined, visitor, - recordTempVariablesInLine ? undefined : hoistVariableDeclaration); + debugger; + return flattenDestructuringToDeclarations( + context, + node, + /*value*/ undefined, + /*skipInitializer*/ false, + recordTempVariablesInLine, + FlattenLevel.All, + visitor + ); } return visitEachChild(node, visitor, context); @@ -2174,7 +2181,7 @@ namespace ts { const temp = createTempVariable(undefined); const newVariableDeclaration = createVariableDeclaration(temp, undefined, undefined, node.variableDeclaration); - const vars = flattenVariableDestructuring(node.variableDeclaration, temp, visitor); + const vars = flattenDestructuringToDeclarations(context, node.variableDeclaration, temp, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.All, visitor); const list = createVariableDeclarationList(vars, /*location*/node.variableDeclaration, /*flags*/node.variableDeclaration.flags); const destructure = createVariableStatement(undefined, list); @@ -2261,7 +2268,7 @@ namespace ts { setEmitFlags(thisArg, EmitFlags.NoSubstitution); } let resultingCall: CallExpression | BinaryExpression; - if (node.transformFlags & TransformFlags.ContainsSpreadExpression) { + if (node.transformFlags & TransformFlags.ContainsSpread) { // [source] // f(...a, b) // x.m(...a, b) @@ -2323,7 +2330,7 @@ namespace ts { */ function visitNewExpression(node: NewExpression): LeftHandSideExpression { // We are here because we contain a SpreadElementExpression. - Debug.assert((node.transformFlags & TransformFlags.ContainsSpreadExpression) !== 0); + Debug.assert((node.transformFlags & TransformFlags.ContainsSpread) !== 0); // [source] // new C(...a) diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 548609d676c..0990e9c454f 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -29,12 +29,6 @@ namespace ts { */ let enabledSubstitutions: ES2017SubstitutionFlags; - /** - * Keeps track of whether we are within any containing namespaces when performing - * just-in-time substitution while printing an expression identifier. - */ - let applicableSubstitutions: ES2017SubstitutionFlags; - /** * This keeps track of containers where `super` is valid, for use with * just-in-time substitution for `super` expressions inside of async methods. @@ -49,8 +43,6 @@ namespace ts { context.onEmitNode = onEmitNode; context.onSubstituteNode = onSubstituteNode; - let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; - return transformSourceFile; function transformSourceFile(node: SourceFile) { @@ -64,13 +56,9 @@ namespace ts { } function visitor(node: Node): VisitResult { - if (node.transformFlags & TransformFlags.ES2017) { + if (node.transformFlags & TransformFlags.ContainsES2017) { return visitorWorker(node); } - else if (node.transformFlags & TransformFlags.ContainsES2017) { - return visitEachChild(node, visitor, context); - } - return node; } @@ -101,8 +89,7 @@ namespace ts { return visitArrowFunction(node); default: - Debug.failBadSyntaxKind(node); - return node; + return visitEachChild(node, visitor, context); } } @@ -133,28 +120,18 @@ namespace ts { * @param node The method node. */ function visitMethodDeclaration(node: MethodDeclaration) { - if (!isAsyncFunctionLike(node)) { - return node; - } - const method = createMethod( + return updateMethod( + node, /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + isAsyncFunctionLike(node) + ? transformAsyncFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setCommentRange(method, node); - setSourceMapRange(method, moveRangePastDecorators(node)); - setOriginalNode(method, node); - - return method; } /** @@ -166,23 +143,18 @@ namespace ts { * @param node The function node. */ function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { - if (!isAsyncFunctionLike(node)) { - return node; - } - const func = createFunctionDeclaration( + return updateFunctionDeclaration( + node, /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + isAsyncFunctionLike(node) + ? transformAsyncFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) ); - setOriginalNode(func, node); - - return func; } /** @@ -194,27 +166,20 @@ namespace ts { * @param node The function expression node. */ function visitFunctionExpression(node: FunctionExpression): Expression { - if (!isAsyncFunctionLike(node)) { - return node; - } if (nodeIsMissing(node.body)) { return createOmittedExpression(); } - - const func = createFunctionExpression( + return updateFunctionExpression( + node, /*modifiers*/ undefined, - node.asteriskToken, node.name, /*typeParameters*/ undefined, visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + isAsyncFunctionLike(node) + ? transformAsyncFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) ); - - setOriginalNode(func, node); - - return func; } /** @@ -223,45 +188,21 @@ namespace ts { * - The node is marked async */ function visitArrowFunction(node: ArrowFunction) { - if (!isAsyncFunctionLike(node)) { - return node; - } - const func = createArrowFunction( + return updateArrowFunction( + node, visitNodes(node.modifiers, visitor, isModifier), /*typeParameters*/ undefined, visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, - node.equalsGreaterThanToken, - transformConciseBody(node), - /*location*/ node + isAsyncFunctionLike(node) + ? transformAsyncFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) ); - - setOriginalNode(func, node); - - return func; } - function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody { - return transformAsyncFunctionBody(node); - } - - function transformConciseBody(node: ArrowFunction): ConciseBody { - return transformAsyncFunctionBody(node); - } - - function transformFunctionBodyWorker(body: Block, start = 0) { - const savedCurrentScope = currentScope; - currentScope = body; - startLexicalEnvironment(); - - const statements = visitNodes(body.statements, visitor, isStatement, start); - const visited = updateBlock(body, statements); - const declarations = endLexicalEnvironment(); - currentScope = savedCurrentScope; - return mergeFunctionBodyLexicalEnvironment(visited, declarations); - } - - function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody | FunctionBody { + function transformAsyncFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody; + function transformAsyncFunctionBody(node: ArrowFunction): ConciseBody; + function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody { const original = getOriginalNode(node, isFunctionLike); const nodeType = original.type; const promiseConstructor = languageVersion < ScriptTarget.ES2015 ? getPromiseConstructor(nodeType) : undefined; @@ -274,7 +215,6 @@ namespace ts { // passed to `__awaiter` is executed inside of the callback to the // promise constructor. - if (!isArrowFunction) { const statements: Statement[] = []; const statementOffset = addPrologueDirectives(statements, (node.body).statements, /*ensureUseStrict*/ false, visitor); @@ -289,6 +229,7 @@ namespace ts { ) ); + addRange(statements, endLexicalEnvironment()); const block = createBlock(statements, /*location*/ node.body, /*multiLine*/ true); // Minor optimization, emit `_super` helper to capture `super` access in an arrow. @@ -307,32 +248,32 @@ namespace ts { return block; } else { - return createAwaiterHelper( + const expression = createAwaiterHelper( currentSourceFileExternalHelpersModuleName, hasLexicalArguments, promiseConstructor, - transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ true) + transformFunctionBodyWorker(node.body) ); + + const declarations = endLexicalEnvironment(); + if (some(declarations)) { + const block = convertToFunctionBody(expression); + return updateBlock(block, createNodeArray(concatenate(block.statements, declarations), block.statements)); + } + + return expression; } } - function transformConciseBodyWorker(body: Block | Expression, forceBlockFunctionBody: boolean) { + function transformFunctionBodyWorker(body: ConciseBody, start?: number) { if (isBlock(body)) { - return transformFunctionBodyWorker(body); + return updateBlock(body, visitLexicalEnvironment(body.statements, visitor, context, start)); } else { startLexicalEnvironment(); - const visited: Expression | Block = visitNode(body, visitor, isConciseBody); + const visited = convertToFunctionBody(visitNode(body, visitor, isConciseBody)); const declarations = endLexicalEnvironment(); - const merged = mergeFunctionBodyLexicalEnvironment(visited, declarations); - if (forceBlockFunctionBody && !isBlock(merged)) { - return createBlock([ - createReturn(merged) - ]); - } - else { - return merged; - } + return updateBlock(visited, createNodeArray(concatenate(visited.statements, declarations), visited.statements)); } } @@ -452,18 +393,17 @@ namespace ts { * @param emit A callback used to emit the node in the printer. */ function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { - const savedApplicableSubstitutions = applicableSubstitutions; - const savedCurrentSuperContainer = currentSuperContainer; // If we need to support substitutions for `super` in an async method, // we should track it here. if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) { + const savedCurrentSuperContainer = currentSuperContainer; currentSuperContainer = node; + previousOnEmitNode(emitContext, node, emitCallback); + currentSuperContainer = savedCurrentSuperContainer; + } + else { + previousOnEmitNode(emitContext, node, emitCallback); } - - previousOnEmitNode(emitContext, node, emitCallback); - - applicableSubstitutions = savedApplicableSubstitutions; - currentSuperContainer = savedCurrentSuperContainer; } /** diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 9013fabf584..a52ee9b1a24 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -4,9 +4,6 @@ /*@internal*/ namespace ts { export function transformESNext(context: TransformationContext) { - const { - hoistVariableDeclaration, - } = context; let currentSourceFile: SourceFile; return transformSourceFile; @@ -20,7 +17,7 @@ namespace ts { return visitorWorker(node); } else if (node.transformFlags & TransformFlags.ContainsESNext) { - return visitEachChild(node, visitor, context); + return visitNodeContainingESNext(node); } else { return node; @@ -32,7 +29,7 @@ namespace ts { case SyntaxKind.ObjectLiteralExpression: return visitObjectLiteralExpression(node as ObjectLiteralExpression); case SyntaxKind.BinaryExpression: - return visitBinaryExpression(node as BinaryExpression); + return visitBinaryExpression(node as BinaryExpression, /*needsDestructuringValue*/ true); case SyntaxKind.VariableDeclaration: return visitVariableDeclaration(node as VariableDeclaration); case SyntaxKind.ForOfStatement: @@ -54,6 +51,16 @@ namespace ts { } } + function visitNodeContainingESNext(node: Node) { + switch (node.kind) { + case SyntaxKind.ExpressionStatement: + return visitExpressionStatement(node as ExpressionStatement); + case SyntaxKind.ParenthesizedExpression: + return visitParenthesizedExpression(node as ParenthesizedExpression, /*needsDestructuringValue*/ true); + } + return visitEachChild(node, visitor, context); + } + function chunkObjectLiteralElements(elements: ObjectLiteralElement[]): Expression[] { let chunkObject: (ShorthandPropertyAssignment | PropertyAssignment)[]; const objects: Expression[] = []; @@ -99,14 +106,43 @@ namespace ts { return createCall(createIdentifier("__assign"), undefined, objects); } + function visitExpressionStatement(node: ExpressionStatement): ExpressionStatement { + switch (node.expression.kind) { + case SyntaxKind.ParenthesizedExpression: + return updateStatement(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); + case SyntaxKind.BinaryExpression: + return updateStatement(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); + } + return visitEachChild(node, visitor, context); + } + + function visitParenthesizedExpression(node: ParenthesizedExpression, needsDestructuringValue: boolean): ParenthesizedExpression { + if (!needsDestructuringValue) { + switch (node.expression.kind) { + case SyntaxKind.ParenthesizedExpression: + return updateParen(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); + case SyntaxKind.BinaryExpression: + return updateParen(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); + } + } + return visitEachChild(node, visitor, context); + } + /** * Visits a BinaryExpression that contains a destructuring assignment. * * @param node A BinaryExpression node. */ - function visitBinaryExpression(node: BinaryExpression): Expression { + function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsESNext) { - return flattenDestructuringAssignment(context, node, /*needsDestructuringValue*/ true, hoistVariableDeclaration, visitor, /*transformRest*/ true); + return flattenDestructuringToExpression( + context, + node, + needsDestructuringValue, + FlattenLevel.ObjectRest, + /*createAssignmentCallback*/ undefined, + visitor + ); } return visitEachChild(node, visitor, context); @@ -120,10 +156,8 @@ namespace ts { function visitVariableDeclaration(node: VariableDeclaration): VisitResult { // If we are here it is because the name contains a binding pattern with a rest somewhere in it. if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.AssertESNext) { - const result = flattenVariableDestructuring(node, /*value*/ undefined, visitor, /*recordTempVariable*/ undefined, /*transformRest*/ true); - return result; + return flattenDestructuringToDeclarations(context, node, /*boundValue*/ undefined, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.ObjectRest, visitor); } - return visitEachChild(node, visitor, context); } @@ -168,14 +202,14 @@ namespace ts { const declaration = firstOrUndefined(initializer.declarations); return declaration && declaration.name && declaration.name.kind === SyntaxKind.ObjectBindingPattern && - !!(declaration.name.transformFlags & TransformFlags.ContainsSpreadExpression); + !!(declaration.name.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)); } return false; } function isRestAssignment(initializer: ForInitializer) { return initializer.kind === SyntaxKind.ObjectLiteralExpression && - initializer.transformFlags & TransformFlags.ContainsSpreadExpression; + initializer.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest); } function visitParameter(node: ParameterDeclaration): ParameterDeclaration { @@ -204,7 +238,7 @@ namespace ts { function isObjectRestParameter(node: ParameterDeclaration) { return node.name && node.name.kind === SyntaxKind.ObjectBindingPattern && - !!(node.name.transformFlags & TransformFlags.ContainsSpreadExpression); + !!(node.name.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)); } function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index ba762cc5f12..8bc09ea45b2 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -27,12 +27,9 @@ namespace ts { } function visitor(node: Node): VisitResult { - if (node.transformFlags & TransformFlags.Jsx) { + if (node.transformFlags & TransformFlags.ContainsJsx) { return visitorWorker(node); } - else if (node.transformFlags & TransformFlags.ContainsJsx) { - return visitEachChild(node, visitor, context); - } else { return node; } @@ -50,8 +47,7 @@ namespace ts { return visitJsxExpression(node); default: - Debug.failBadSyntaxKind(node); - return undefined; + return visitEachChild(node, visitor, context); } } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 87d02b44025..bb17b99e5c6 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -19,8 +19,7 @@ namespace ts { const { startLexicalEnvironment, - endLexicalEnvironment, - hoistVariableDeclaration, + endLexicalEnvironment } = context; const compilerOptions = context.getCompilerOptions(); @@ -757,10 +756,13 @@ namespace ts { */ function transformInitializedVariable(node: VariableDeclaration): Expression { if (isBindingPattern(node.name)) { - return flattenVariableDestructuringToExpression( + return flattenDestructuringToExpression( + context, node, - hoistVariableDeclaration, - createExportExpression + /*needsValue*/ false, + FlattenLevel.All, + createExportExpression, + /*visitor*/ undefined ); } else { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 0524b9354d2..1ed1f82d506 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -808,7 +808,7 @@ namespace ts { function transformInitializedVariable(node: VariableDeclaration, isExportedDeclaration: boolean): Expression { const createAssignment = isExportedDeclaration ? createExportedVariableAssignment : createNonExportedVariableAssignment; return isBindingPattern(node.name) - ? flattenVariableDestructuringToExpression(node, hoistVariableDeclaration, createAssignment, destructuringVisitor) + ? flattenDestructuringToExpression(context, node, /*needsValue*/ false, FlattenLevel.All, createAssignment, destructuringVisitor) : createAssignment(node.name, visitNode(node.initializer, destructuringVisitor, isExpression)); } @@ -1459,7 +1459,13 @@ namespace ts { */ function visitDestructuringAssignment(node: DestructuringAssignment): VisitResult { if (hasExportedReferenceInDestructuringTarget(node.left)) { - return flattenDestructuringAssignment(context, node, /*needsValue*/ true, hoistVariableDeclaration, destructuringVisitor); + return flattenDestructuringToExpression( + context, + node, + /*needsValue*/ true, + FlattenLevel.All, + /*createAssignmentCallback*/ undefined, + destructuringVisitor); } return visitEachChild(node, destructuringVisitor, context); diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 89fa039100a..118b170b6a4 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2353,9 +2353,11 @@ namespace ts { function transformInitializedVariable(node: VariableDeclaration): Expression { const name = node.name; if (isBindingPattern(name)) { - return flattenVariableDestructuringToExpression( + return flattenDestructuringToExpression( + context, node, - hoistVariableDeclaration, + /*needsValue*/ false, + FlattenLevel.All, createNamespaceExportExpression, visitor ); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e8018083783..e0ba546fb96 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -506,6 +506,7 @@ namespace ts { export interface NodeArray extends Array, TextRange { hasTrailingComma?: boolean; + /* @internal */ transformFlags?: TransformFlags; } export interface Token extends Node { @@ -3495,45 +3496,46 @@ namespace ts { // - Flags used to indicate that a node or subtree contains syntax that requires transformation. TypeScript = 1 << 0, ContainsTypeScript = 1 << 1, - Jsx = 1 << 2, - ContainsJsx = 1 << 3, - ESNext = 1 << 4, - ContainsESNext = 1 << 5, - ES2017 = 1 << 6, - ContainsES2017 = 1 << 7, - ES2016 = 1 << 8, - ContainsES2016 = 1 << 9, - ES2015 = 1 << 10, - ContainsES2015 = 1 << 11, - Generator = 1 << 12, - ContainsGenerator = 1 << 13, - DestructuringAssignment = 1 << 14, - ContainsDestructuringAssignment = 1 << 15, + ContainsJsx = 1 << 2, + ESNext = 1 << 3, + ContainsESNext = 1 << 4, + ContainsES2017 = 1 << 5, + ES2016 = 1 << 6, + ContainsES2016 = 1 << 7, + ES2015 = 1 << 8, + ContainsES2015 = 1 << 9, + Generator = 1 << 10, + ContainsGenerator = 1 << 11, + DestructuringAssignment = 1 << 12, + ContainsDestructuringAssignment = 1 << 13, // Markers // - Flags used to indicate that a subtree contains a specific transformation. - ContainsDecorators = 1 << 16, - ContainsPropertyInitializer = 1 << 17, - ContainsLexicalThis = 1 << 18, - ContainsCapturedLexicalThis = 1 << 19, - ContainsLexicalThisInComputedPropertyName = 1 << 20, - ContainsDefaultValueAssignments = 1 << 21, - ContainsParameterPropertyAssignments = 1 << 22, - ContainsSpreadExpression = 1 << 23, - ContainsComputedPropertyName = 1 << 24, - ContainsBlockScopedBinding = 1 << 25, - ContainsBindingPattern = 1 << 26, - ContainsYield = 1 << 27, - ContainsHoistedDeclarationOrCompletion = 1 << 28, + ContainsDecorators = 1 << 14, + ContainsPropertyInitializer = 1 << 15, + ContainsLexicalThis = 1 << 16, + ContainsCapturedLexicalThis = 1 << 17, + ContainsLexicalThisInComputedPropertyName = 1 << 18, + ContainsDefaultValueAssignments = 1 << 19, + ContainsParameterPropertyAssignments = 1 << 20, + ContainsSpread = 1 << 21, + ContainsRest = ContainsSpread, + ContainsObjectSpread = 1 << 22, + ContainsObjectRest = ContainsObjectSpread, + ContainsComputedPropertyName = 1 << 23, + ContainsBlockScopedBinding = 1 << 24, + ContainsBindingPattern = 1 << 25, + ContainsYield = 1 << 26, + ContainsHoistedDeclarationOrCompletion = 1 << 27, HasComputedFlags = 1 << 29, // 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, - AssertJsx = Jsx | ContainsJsx, + AssertJsx = ContainsJsx, AssertESNext = ESNext | ContainsESNext, - AssertES2017 = ES2017 | ContainsES2017, + AssertES2017 = ContainsES2017, AssertES2016 = ES2016 | ContainsES2016, AssertES2015 = ES2015 | ContainsES2015, AssertGenerator = Generator | ContainsGenerator, @@ -3542,18 +3544,20 @@ 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 | ESNext | ES2017 | ES2016 | ES2015 | DestructuringAssignment | Generator | HasComputedFlags, - ArrowFunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, - FunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, - ConstructorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, - MethodOrAccessorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, + NodeExcludes = TypeScript | ESNext | ES2016 | ES2015 | DestructuringAssignment | Generator | HasComputedFlags, + ArrowFunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, + FunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, + ConstructorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, + MethodOrAccessorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, ClassExcludes = NodeExcludes | ContainsDecorators | ContainsPropertyInitializer | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsParameterPropertyAssignments | ContainsLexicalThisInComputedPropertyName, ModuleExcludes = NodeExcludes | ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsHoistedDeclarationOrCompletion, TypeExcludes = ~ContainsTypeScript, - ObjectLiteralExcludes = NodeExcludes | ContainsDecorators | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName, - ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsSpreadExpression, - VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern, - ParameterExcludes = NodeExcludes | ContainsBindingPattern, + ObjectLiteralExcludes = NodeExcludes | ContainsDecorators | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName | ContainsObjectSpread, + ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsSpread, + VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern | ContainsObjectSpread, + ParameterExcludes = NodeExcludes, + CatchClauseExcludes = NodeExcludes | ContainsObjectSpread, + BindingPatternExcludes = NodeExcludes | ContainsSpread, // Masks // - Additional bitmasks @@ -3622,7 +3626,6 @@ namespace ts { endLexicalEnvironment(): Statement[]; } - export interface TextSpan { start: number; length: number; diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index ebb3ec01ed3..825e0b45e45 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -99,20 +99,26 @@ namespace ts { return node ? f(initial, node) : initial; } + function reduceNodeArray(nodes: Node[], f: (memo: T, nodes: Node[]) => T, initial: T) { + return nodes ? f(initial, nodes) : initial; + } + /** * Similar to `reduceLeft`, performs a reduction against each child of a node. * NOTE: Unlike `forEachChild`, this does *not* visit every node. Only nodes added to the * `nodeEdgeTraversalMap` above will be visited. * * @param node The node containing the children to reduce. - * @param f The callback function * @param initial The initial value to supply to the reduction. + * @param f The callback function */ - export function reduceEachChild(node: Node, f: (memo: T, node: Node) => T, initial: T): T { + export function reduceEachChild(node: Node, initial: T, cbNode: (memo: T, node: Node) => T, cbNodeArray?: (memo: T, nodes: Node[]) => T): T { if (node === undefined) { return initial; } + const reduceNodes: (nodes: Node[], f: (memo: T, node: Node | Node[]) => T, initial: T) => T = cbNodeArray ? reduceNodeArray : reduceLeft; + const cbNodes = cbNodeArray || cbNode; const kind = node.kind; // No need to visit nodes with no children. @@ -138,127 +144,127 @@ namespace ts { // Names case SyntaxKind.ComputedPropertyName: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; // Signature elements case SyntaxKind.Parameter: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; case SyntaxKind.Decorator: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; // Type member case SyntaxKind.PropertyDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; case SyntaxKind.MethodDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.Constructor: - result = reduceLeft((node).modifiers, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.GetAccessor: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.SetAccessor: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).body, cbNode, result); break; // Binding patterns case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: - result = reduceLeft((node).elements, f, result); + result = reduceNodes((node).elements, cbNodes, result); break; case SyntaxKind.BindingElement: - result = reduceNode((node).propertyName, f, result); - result = reduceNode((node).name, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNode((node).propertyName, cbNode, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; // Expression case SyntaxKind.ArrayLiteralExpression: - result = reduceLeft((node).elements, f, result); + result = reduceNodes((node).elements, cbNodes, result); break; case SyntaxKind.ObjectLiteralExpression: - result = reduceLeft((node).properties, f, result); + result = reduceNodes((node).properties, cbNodes, result); break; case SyntaxKind.PropertyAccessExpression: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).name, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).name, cbNode, result); break; case SyntaxKind.ElementAccessExpression: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).argumentExpression, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).argumentExpression, cbNode, result); break; case SyntaxKind.CallExpression: - result = reduceNode((node).expression, f, result); - result = reduceLeft((node).typeArguments, f, result); - result = reduceLeft((node).arguments, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNodes((node).typeArguments, cbNodes, result); + result = reduceNodes((node).arguments, cbNodes, result); break; case SyntaxKind.NewExpression: - result = reduceNode((node).expression, f, result); - result = reduceLeft((node).typeArguments, f, result); - result = reduceLeft((node).arguments, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNodes((node).typeArguments, cbNodes, result); + result = reduceNodes((node).arguments, cbNodes, result); break; case SyntaxKind.TaggedTemplateExpression: - result = reduceNode((node).tag, f, result); - result = reduceNode((node).template, f, result); + result = reduceNode((node).tag, cbNode, result); + result = reduceNode((node).template, cbNode, result); break; case SyntaxKind.FunctionExpression: - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.ArrowFunction: - result = reduceLeft((node).modifiers, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.ParenthesizedExpression: @@ -269,258 +275,258 @@ namespace ts { case SyntaxKind.YieldExpression: case SyntaxKind.SpreadElement: case SyntaxKind.NonNullExpression: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.PostfixUnaryExpression: - result = reduceNode((node).operand, f, result); + result = reduceNode((node).operand, cbNode, result); break; case SyntaxKind.BinaryExpression: - result = reduceNode((node).left, f, result); - result = reduceNode((node).right, f, result); + result = reduceNode((node).left, cbNode, result); + result = reduceNode((node).right, cbNode, result); break; case SyntaxKind.ConditionalExpression: - result = reduceNode((node).condition, f, result); - result = reduceNode((node).whenTrue, f, result); - result = reduceNode((node).whenFalse, f, result); + result = reduceNode((node).condition, cbNode, result); + result = reduceNode((node).whenTrue, cbNode, result); + result = reduceNode((node).whenFalse, cbNode, result); break; case SyntaxKind.TemplateExpression: - result = reduceNode((node).head, f, result); - result = reduceLeft((node).templateSpans, f, result); + result = reduceNode((node).head, cbNode, result); + result = reduceNodes((node).templateSpans, cbNodes, result); break; case SyntaxKind.ClassExpression: - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).heritageClauses, f, result); - result = reduceLeft((node).members, f, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).heritageClauses, cbNodes, result); + result = reduceNodes((node).members, cbNodes, result); break; case SyntaxKind.ExpressionWithTypeArguments: - result = reduceNode((node).expression, f, result); - result = reduceLeft((node).typeArguments, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNodes((node).typeArguments, cbNodes, result); break; // Misc case SyntaxKind.TemplateSpan: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).literal, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).literal, cbNode, result); break; // Element case SyntaxKind.Block: - result = reduceLeft((node).statements, f, result); + result = reduceNodes((node).statements, cbNodes, result); break; case SyntaxKind.VariableStatement: - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).declarationList, f, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).declarationList, cbNode, result); break; case SyntaxKind.ExpressionStatement: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.IfStatement: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).thenStatement, f, result); - result = reduceNode((node).elseStatement, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).thenStatement, cbNode, result); + result = reduceNode((node).elseStatement, cbNode, result); break; case SyntaxKind.DoStatement: - result = reduceNode((node).statement, f, result); - result = reduceNode((node).expression, f, result); + result = reduceNode((node).statement, cbNode, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.WhileStatement: case SyntaxKind.WithStatement: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).statement, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).statement, cbNode, result); break; case SyntaxKind.ForStatement: - result = reduceNode((node).initializer, f, result); - result = reduceNode((node).condition, f, result); - result = reduceNode((node).incrementor, f, result); - result = reduceNode((node).statement, f, result); + result = reduceNode((node).initializer, cbNode, result); + result = reduceNode((node).condition, cbNode, result); + result = reduceNode((node).incrementor, cbNode, result); + result = reduceNode((node).statement, cbNode, result); break; case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: - result = reduceNode((node).initializer, f, result); - result = reduceNode((node).expression, f, result); - result = reduceNode((node).statement, f, result); + result = reduceNode((node).initializer, cbNode, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).statement, cbNode, result); break; case SyntaxKind.ReturnStatement: case SyntaxKind.ThrowStatement: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.SwitchStatement: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).caseBlock, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).caseBlock, cbNode, result); break; case SyntaxKind.LabeledStatement: - result = reduceNode((node).label, f, result); - result = reduceNode((node).statement, f, result); + result = reduceNode((node).label, cbNode, result); + result = reduceNode((node).statement, cbNode, result); break; case SyntaxKind.TryStatement: - result = reduceNode((node).tryBlock, f, result); - result = reduceNode((node).catchClause, f, result); - result = reduceNode((node).finallyBlock, f, result); + result = reduceNode((node).tryBlock, cbNode, result); + result = reduceNode((node).catchClause, cbNode, result); + result = reduceNode((node).finallyBlock, cbNode, result); break; case SyntaxKind.VariableDeclaration: - result = reduceNode((node).name, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; case SyntaxKind.VariableDeclarationList: - result = reduceLeft((node).declarations, f, result); + result = reduceNodes((node).declarations, cbNodes, result); break; case SyntaxKind.FunctionDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.ClassDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).heritageClauses, f, result); - result = reduceLeft((node).members, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).heritageClauses, cbNodes, result); + result = reduceNodes((node).members, cbNodes, result); break; case SyntaxKind.CaseBlock: - result = reduceLeft((node).clauses, f, result); + result = reduceNodes((node).clauses, cbNodes, result); break; case SyntaxKind.ImportDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).importClause, f, result); - result = reduceNode((node).moduleSpecifier, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).importClause, cbNode, result); + result = reduceNode((node).moduleSpecifier, cbNode, result); break; case SyntaxKind.ImportClause: - result = reduceNode((node).name, f, result); - result = reduceNode((node).namedBindings, f, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).namedBindings, cbNode, result); break; case SyntaxKind.NamespaceImport: - result = reduceNode((node).name, f, result); + result = reduceNode((node).name, cbNode, result); break; case SyntaxKind.NamedImports: case SyntaxKind.NamedExports: - result = reduceLeft((node).elements, f, result); + result = reduceNodes((node).elements, cbNodes, result); break; case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - result = reduceNode((node).propertyName, f, result); - result = reduceNode((node).name, f, result); + result = reduceNode((node).propertyName, cbNode, result); + result = reduceNode((node).name, cbNode, result); break; case SyntaxKind.ExportAssignment: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).expression, f, result); + result = reduceLeft((node).decorators, cbNode, result); + result = reduceLeft((node).modifiers, cbNode, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.ExportDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).exportClause, f, result); - result = reduceNode((node).moduleSpecifier, f, result); + result = reduceLeft((node).decorators, cbNode, result); + result = reduceLeft((node).modifiers, cbNode, result); + result = reduceNode((node).exportClause, cbNode, result); + result = reduceNode((node).moduleSpecifier, cbNode, result); break; // JSX case SyntaxKind.JsxElement: - result = reduceNode((node).openingElement, f, result); - result = reduceLeft((node).children, f, result); - result = reduceNode((node).closingElement, f, result); + result = reduceNode((node).openingElement, cbNode, result); + result = reduceLeft((node).children, cbNode, result); + result = reduceNode((node).closingElement, cbNode, result); break; case SyntaxKind.JsxSelfClosingElement: case SyntaxKind.JsxOpeningElement: - result = reduceNode((node).tagName, f, result); - result = reduceLeft((node).attributes, f, result); + result = reduceNode((node).tagName, cbNode, result); + result = reduceNodes((node).attributes, cbNodes, result); break; case SyntaxKind.JsxClosingElement: - result = reduceNode((node).tagName, f, result); + result = reduceNode((node).tagName, cbNode, result); break; case SyntaxKind.JsxAttribute: - result = reduceNode((node).name, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; case SyntaxKind.JsxSpreadAttribute: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.JsxExpression: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; // Clauses case SyntaxKind.CaseClause: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); // fall-through case SyntaxKind.DefaultClause: - result = reduceLeft((node).statements, f, result); + result = reduceNodes((node).statements, cbNodes, result); break; case SyntaxKind.HeritageClause: - result = reduceLeft((node).types, f, result); + result = reduceNodes((node).types, cbNodes, result); break; case SyntaxKind.CatchClause: - result = reduceNode((node).variableDeclaration, f, result); - result = reduceNode((node).block, f, result); + result = reduceNode((node).variableDeclaration, cbNode, result); + result = reduceNode((node).block, cbNode, result); break; // Property assignments case SyntaxKind.PropertyAssignment: - result = reduceNode((node).name, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; case SyntaxKind.ShorthandPropertyAssignment: - result = reduceNode((node).name, f, result); - result = reduceNode((node).objectAssignmentInitializer, f, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).objectAssignmentInitializer, cbNode, result); break; case SyntaxKind.SpreadAssignment: - result = reduceNode((node as SpreadAssignment).expression, f, result); + result = reduceNode((node as SpreadAssignment).expression, cbNode, result); break; // Top-level nodes case SyntaxKind.SourceFile: - result = reduceLeft((node).statements, f, result); + result = reduceNodes((node).statements, cbNodes, result); break; case SyntaxKind.PartiallyEmittedExpression: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; default: @@ -530,8 +536,8 @@ namespace ts { const value = (>node)[edge.name]; if (value !== undefined) { result = isArray(value) - ? reduceLeft(>value, f, result) - : f(result, value); + ? reduceNodes(>value, cbNodes, result) + : cbNode(result, value); } } } @@ -553,8 +559,8 @@ namespace ts { export function visitNode(node: T, visitor: (node: Node) => VisitResult, 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, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): T; export function visitNode(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; + if (node === undefined || visitor === undefined) { + return node; } aggregateTransformFlags(node); @@ -659,6 +665,30 @@ namespace ts { return updated || nodes; } + export function visitLexicalEnvironment(nodes: NodeArray, visitor: (node: Node) => VisitResult, context: LexicalEnvironment, start?: number) { + context.startLexicalEnvironment(); + const updated = visitNodes(nodes, visitor, isStatement, start); + const declarations = context.endLexicalEnvironment(); + return createNodeArray(concatenate(updated, declarations), updated); + } + + export function visitParameterList(nodes: NodeArray, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { + context.startLexicalEnvironment(); + return visitNodes(nodes, visitor, isParameter); + } + + export function visitFunctionBody(node: FunctionBody, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): FunctionBody; + export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): ConciseBody; + export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { + const visited = visitNode(node, visitor, isConciseBody); + const declarations = context.endLexicalEnvironment(); + if (some(declarations)) { + const block = convertToFunctionBody(visited); + return updateBlock(block, createNodeArray(concatenate(block.statements, declarations), block.statements)); + } + return visited; + } + /** * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place. * @@ -721,41 +751,33 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.Constructor: return updateConstructor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitParameterList((node).parameters, visitor, context), + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.GetAccessor: return updateGetAccessor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.SetAccessor: return updateSetAccessor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitParameterList((node).parameters, visitor, context), + visitFunctionBody((node).body, visitor, context)); // Binding patterns case SyntaxKind.ObjectBindingPattern: @@ -818,21 +840,17 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.ArrowFunction: return updateArrowFunction(node, visitNodes((node).modifiers, visitor, isModifier), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isConciseBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.DeleteExpression: return updateDelete(node, @@ -1003,11 +1021,9 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.ClassDeclaration: return updateClassDeclaration(node, @@ -1139,13 +1155,8 @@ namespace ts { // Top-level nodes case SyntaxKind.SourceFile: - context.startLexicalEnvironment(); return updateSourceFileNode(node, - createNodeArray( - concatenate( - visitNodes((node).statements, visitor, isStatement), - context.endLexicalEnvironment()), - (node).statements)); + visitLexicalEnvironment((node).statements, visitor, context)); // Transformation nodes case SyntaxKind.PartiallyEmittedExpression: @@ -1249,13 +1260,25 @@ namespace ts { if (node === undefined) { return TransformFlags.None; } - else if (node.transformFlags & TransformFlags.HasComputedFlags) { + if (node.transformFlags & TransformFlags.HasComputedFlags) { return node.transformFlags & ~getTransformFlagsSubtreeExclusions(node.kind); } - else { - const subtreeFlags = aggregateTransformFlagsForSubtree(node); - return computeTransformFlagsForNode(node, subtreeFlags); + const subtreeFlags = aggregateTransformFlagsForSubtree(node); + return computeTransformFlagsForNode(node, subtreeFlags); + } + + function aggregateTransformFlagsForNodeArray(nodes: NodeArray): TransformFlags { + if (nodes === undefined) { + return TransformFlags.None; } + let subtreeFlags = TransformFlags.None; + let nodeArrayFlags = TransformFlags.None; + for (const node of nodes) { + subtreeFlags |= aggregateTransformFlagsForNode(node); + nodeArrayFlags |= node.transformFlags & ~TransformFlags.HasComputedFlags; + } + nodes.transformFlags = nodeArrayFlags | TransformFlags.HasComputedFlags; + return subtreeFlags; } /** @@ -1269,15 +1292,19 @@ namespace ts { } // Aggregate the transform flags of each child. - return reduceEachChild(node, aggregateTransformFlagsForChildNode, TransformFlags.None); + return reduceEachChild(node, TransformFlags.None, aggregateTransformFlagsForChildNode, aggregateTransformFlagsForChildNodes); } /** * Aggregates the TransformFlags of a child node with the TransformFlags of its * siblings. */ - function aggregateTransformFlagsForChildNode(transformFlags: TransformFlags, child: Node): TransformFlags { - return transformFlags | aggregateTransformFlagsForNode(child); + function aggregateTransformFlagsForChildNode(transformFlags: TransformFlags, node: Node): TransformFlags { + return transformFlags | aggregateTransformFlagsForNode(node); + } + + function aggregateTransformFlagsForChildNodes(transformFlags: TransformFlags, nodes: NodeArray): TransformFlags { + return transformFlags | aggregateTransformFlagsForNodeArray(nodes); } export namespace Debug { diff --git a/tests/baselines/reference/assignmentTypeNarrowing.js b/tests/baselines/reference/assignmentTypeNarrowing.js index 7c10dde65cc..92fd49d9941 100644 --- a/tests/baselines/reference/assignmentTypeNarrowing.js +++ b/tests/baselines/reference/assignmentTypeNarrowing.js @@ -37,17 +37,17 @@ x = [true][0]; x; // boolean _a = [1][0], x = _a === void 0 ? "" : _a; x; // string | number -(_b = { x: true }, x = _b.x, _b); +(x = { x: true }.x); x; // boolean -(_c = { y: 1 }, x = _c.y, _c); +(x = { y: 1 }.y); x; // number -(_d = { x: true }, _e = _d.x, x = _e === void 0 ? "" : _e, _d); +(_b = { x: true }.x, x = _b === void 0 ? "" : _b); x; // string | boolean -(_f = { y: 1 }, _g = _f.y, x = _g === void 0 ? /a/ : _g, _f); +(_c = { y: 1 }.y, x = _c === void 0 ? /a/ : _c); x; // number | RegExp var a; for (var _i = 0, a_1 = a; _i < a_1.length; _i++) { x = a_1[_i]; x; // string } -var _a, _b, _c, _d, _e, _f, _g; +var _a, _b, _c; diff --git a/tests/baselines/reference/asyncMethodWithSuper_es5.js b/tests/baselines/reference/asyncMethodWithSuper_es5.js index 9ceff6bab4e..235f2763f9f 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es5.js +++ b/tests/baselines/reference/asyncMethodWithSuper_es5.js @@ -81,8 +81,8 @@ var B = (function (_super) { // async method with assignment/destructuring on 'super' requires a binding B.prototype.advanced = function () { return __awaiter(this, void 0, void 0, function () { - var f, a, b, _a, _b; - return __generator(this, function (_c) { + var f, a, b; + return __generator(this, function (_a) { f = function () { }; // call with property access _super.prototype.x.call(this); @@ -95,9 +95,9 @@ var B = (function (_super) { // element access (assign) _super.prototype["x"] = f; // destructuring assign with property access - (_a = { f: f }, super.x = _a.f, _a); + (_super.prototype.x = { f: f }.f); // destructuring assign with element access - (_b = { f: f }, super["x"] = _b.f, _b); + (_super.prototype["x"] = { f: f }.f); return [2 /*return*/]; }); }); diff --git a/tests/baselines/reference/computedPropertiesInDestructuring1.js b/tests/baselines/reference/computedPropertiesInDestructuring1.js index 0bc4286ed7b..b0dc33a4b4e 100644 --- a/tests/baselines/reference/computedPropertiesInDestructuring1.js +++ b/tests/baselines/reference/computedPropertiesInDestructuring1.js @@ -65,11 +65,11 @@ function f5(_a) { var _f = foo(), bar6 = [{ bar: "bar" }][0][_f]; var _g = foo.toExponential(), bar7 = [{ bar: "bar" }][0][_g]; // destructuring assignment -(_h = { bar: "bar" }, _j = foo, bar = _h[_j], _h); -(_k = { bar: "bar" }, _l = "bar", bar2 = _k[_l], _k); -(_m = { bar: "bar" }, _o = foo2(), bar3 = _m[_o], _m); -_p = foo, bar4 = [{ bar: "bar" }][0][_p]; -_q = foo2(), bar5 = [{ bar: "bar" }][0][_q]; -_r = foo(), bar4 = [{ bar: "bar" }][0][_r]; -_s = (1 + {}), bar4 = [{ bar: "bar" }][0][_s]; -var _h, _j, _k, _l, _m, _o, _p, _q, _r, _s; +(_h = foo, bar = { bar: "bar" }[_h]); +(_j = "bar", bar2 = { bar: "bar" }[_j]); +(_k = foo2(), bar3 = { bar: "bar" }[_k]); +_l = foo, bar4 = [{ bar: "bar" }][0][_l]; +_m = foo2(), bar5 = [{ bar: "bar" }][0][_m]; +_o = foo(), bar4 = [{ bar: "bar" }][0][_o]; +_p = (1 + {}), bar4 = [{ bar: "bar" }][0][_p]; +var _h, _j, _k, _l, _m, _o, _p; diff --git a/tests/baselines/reference/declarationsAndAssignments.js b/tests/baselines/reference/declarationsAndAssignments.js index 018aa5522ca..3a2f9ef3b4e 100644 --- a/tests/baselines/reference/declarationsAndAssignments.js +++ b/tests/baselines/reference/declarationsAndAssignments.js @@ -300,8 +300,8 @@ function f18() { var a; var b; var aa; - (_a = { a: a, b: b }, a = _a.a, b = _a.b, _a); - (_b = { b: b, a: a }, a = _b.a, b = _b.b, _b); + (_a = { a: a, b: b }, a = _a.a, b = _a.b); + (_b = { b: b, a: a }, a = _b.a, b = _b.b); _c = [a, b], aa[0] = _c[0], b = _c[1]; _d = [b, a], a = _d[0], b = _d[1]; // Error _e = [2, "def"], _f = _e[0], a = _f === void 0 ? 1 : _f, _g = _e[1], b = _g === void 0 ? "abc" : _g; @@ -311,7 +311,7 @@ function f19() { var a, b; _a = [1, 2], a = _a[0], b = _a[1]; _b = [b, a], a = _b[0], b = _b[1]; - (_c = { b: b, a: a }, a = _c.a, b = _c.b, _c); + (_c = { b: b, a: a }, a = _c.a, b = _c.b); _d = [[2, 3]][0], _e = _d === void 0 ? [1, 2] : _d, a = _e[0], b = _e[1]; var x = (_f = [1, 2], a = _f[0], b = _f[1], _f); var _a, _b, _c, _d, _e, _f; diff --git a/tests/baselines/reference/destructuringAssignmentWithDefault.js b/tests/baselines/reference/destructuringAssignmentWithDefault.js index ce3837e1162..fe3ccdcee53 100644 --- a/tests/baselines/reference/destructuringAssignmentWithDefault.js +++ b/tests/baselines/reference/destructuringAssignmentWithDefault.js @@ -7,5 +7,5 @@ let x = 0; //// [destructuringAssignmentWithDefault.js] var a = {}; var x = 0; -(_a = a.x, x = _a === void 0 ? 1 : _a, a); +(_a = a.x, x = _a === void 0 ? 1 : _a); var _a; diff --git a/tests/baselines/reference/emptyAssignmentPatterns02_ES5.js b/tests/baselines/reference/emptyAssignmentPatterns02_ES5.js index 370e019104f..f5434d4a1c3 100644 --- a/tests/baselines/reference/emptyAssignmentPatterns02_ES5.js +++ b/tests/baselines/reference/emptyAssignmentPatterns02_ES5.js @@ -9,8 +9,8 @@ let x, y, z, a1, a2, a3; //// [emptyAssignmentPatterns02_ES5.js] var a; var x, y, z, a1, a2, a3; -(x = a.x, y = a.y, z = a.z, a); -(a1 = a[0], a2 = a[1], a3 = a[2], a); +(x = a.x, y = a.y, z = a.z); +(a1 = a[0], a2 = a[1], a3 = a[2]); //// [emptyAssignmentPatterns02_ES5.d.ts] diff --git a/tests/baselines/reference/emptyAssignmentPatterns04_ES5.js b/tests/baselines/reference/emptyAssignmentPatterns04_ES5.js index e6b3cc7e3f2..91559d18252 100644 --- a/tests/baselines/reference/emptyAssignmentPatterns04_ES5.js +++ b/tests/baselines/reference/emptyAssignmentPatterns04_ES5.js @@ -9,9 +9,8 @@ let x, y, z, a1, a2, a3; //// [emptyAssignmentPatterns04_ES5.js] var a; var x, y, z, a1, a2, a3; -(_a = a, x = _a.x, y = _a.y, z = _a.z, _a); -(_b = a, a1 = _b[0], a2 = _b[1], a3 = _b[2], _b); -var _a, _b; +(x = a.x, y = a.y, z = a.z); +(a1 = a[0], a2 = a[1], a3 = a[2]); //// [emptyAssignmentPatterns04_ES5.d.ts] diff --git a/tests/baselines/reference/initializePropertiesWithRenamedLet.js b/tests/baselines/reference/initializePropertiesWithRenamedLet.js index d53fed8c0f7..4abe488aa08 100644 --- a/tests/baselines/reference/initializePropertiesWithRenamedLet.js +++ b/tests/baselines/reference/initializePropertiesWithRenamedLet.js @@ -28,7 +28,6 @@ if (true) { var x_1 = { x: 0 }.x; var y_1 = { y: 0 }.y; var z_1; - (_a = { z: 0 }, z_1 = _a.z, _a); - (_b = { z: 0 }, z_1 = _b.z, _b); + (z_1 = { z: 0 }.z); + (z_1 = { z: 0 }.z); } -var _a, _b; diff --git a/tests/baselines/reference/missingAndExcessProperties.js b/tests/baselines/reference/missingAndExcessProperties.js index b9fcadd9519..daefe18eed7 100644 --- a/tests/baselines/reference/missingAndExcessProperties.js +++ b/tests/baselines/reference/missingAndExcessProperties.js @@ -45,10 +45,10 @@ function f1() { // Missing properties function f2() { var x, y; - (_a = {}, x = _a.x, y = _a.y, _a); - (_b = {}, _c = _b.x, x = _c === void 0 ? 1 : _c, y = _b.y, _b); - (_d = {}, x = _d.x, _e = _d.y, y = _e === void 0 ? 1 : _e, _d); - (_f = {}, _g = _f.x, x = _g === void 0 ? 1 : _g, _h = _f.y, y = _h === void 0 ? 1 : _h, _f); + (_a = {}, x = _a.x, y = _a.y); + (_b = {}, _c = _b.x, x = _c === void 0 ? 1 : _c, y = _b.y); + (_d = {}, x = _d.x, _e = _d.y, y = _e === void 0 ? 1 : _e); + (_f = {}, _g = _f.x, x = _g === void 0 ? 1 : _g, _h = _f.y, y = _h === void 0 ? 1 : _h); var _a, _b, _c, _d, _e, _f, _g, _h; } // Excess properties @@ -62,8 +62,8 @@ function f3() { function f4() { var x, y; ({ x: 0, y: 0 }); - (_a = { x: 0, y: 0 }, x = _a.x, _a); - (_b = { x: 0, y: 0 }, y = _b.y, _b); - (_c = { x: 0, y: 0 }, x = _c.x, y = _c.y, _c); - var _a, _b, _c; + (x = { x: 0, y: 0 }.x); + (y = { x: 0, y: 0 }.y); + (_a = { x: 0, y: 0 }, x = _a.x, y = _a.y); + var _a; } diff --git a/tests/baselines/reference/objectRest.js b/tests/baselines/reference/objectRest.js index a295ff7b9f1..519283b32e5 100644 --- a/tests/baselines/reference/objectRest.js +++ b/tests/baselines/reference/objectRest.js @@ -53,9 +53,9 @@ let nestedrest; var { x } = nestedrest, _a = nestedrest.n1, { y } = _a, _b = _a.n2, { z } = _b, nr = __rest(_b.n3, []), restrest = __rest(nestedrest, ["x", "n1"]); let complex; var _c = complex.x, { ka } = _c, nested = __rest(_c, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"]); -(_d = complex.x, { ka } = _d, nested = __rest(_d, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"]), complex); +(_d = complex.x, { ka } = _d, nested = __rest(_d, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"])); var _e = { x: 1, y: 2 }, { x } = _e, fresh = __rest(_e, ["x"]); -(_f = { x: 1, y: 2 }, { x } = _f, fresh = __rest(_f, ["x"]), _f); +(_f = { x: 1, y: 2 }, { x } = _f, fresh = __rest(_f, ["x"])); class Removable { set z(value) { } get both() { return 12; } diff --git a/tests/baselines/reference/objectRestAssignment.js b/tests/baselines/reference/objectRestAssignment.js index 41620d59b58..d4d266d3fe5 100644 --- a/tests/baselines/reference/objectRestAssignment.js +++ b/tests/baselines/reference/objectRestAssignment.js @@ -26,10 +26,10 @@ let nested; let other; let rest; let complex; -(_a = complex.x, { ka } = _a, nested = __rest(_a, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"]), complex); +(_a = complex.x, { ka } = _a, nested = __rest(_a, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"])); // should be: let overEmit; // var _g = overEmit.a, [_h, ...y] = _g, nested2 = __rest(_h, []), _j = overEmit.b, { z } = _j, c = __rest(_j, ["z"]), rest2 = __rest(overEmit, ["a", "b"]); var _b = overEmit.a, [_c, ...y] = _b, nested2 = __rest(_c, []), _d = overEmit.b, { z } = _d, c = __rest(_d, ["z"]), rest2 = __rest(overEmit, ["a", "b"]); -(_e = overEmit.a, [_f, ...y] = _e, nested2 = __rest(_f, []), _g = overEmit.b, { z } = _g, c = __rest(_g, ["z"]), rest2 = __rest(overEmit, ["a", "b"]), overEmit); -var _a, _e, _f, _g; +(_e = overEmit.a, [_f, ...y] = _e, nested2 = __rest(_f, []), _g = overEmit.b, { z } = _g, c = __rest(_g, ["z"]), rest2 = __rest(overEmit, ["a", "b"])); +var _a, _e, _g; diff --git a/tests/baselines/reference/objectRestNegative.js b/tests/baselines/reference/objectRestNegative.js index b3b49740762..6a710a22870 100644 --- a/tests/baselines/reference/objectRestNegative.js +++ b/tests/baselines/reference/objectRestNegative.js @@ -17,9 +17,9 @@ var __rest = (this && this.__rest) || function (s, e) { return t; }; var o = { a: 1, b: 'no' }; -var mustBeLast = o.mustBeLast, a = o.a; +var a = o.a; function stillMustBeLast(_a) { - var mustBeLast = _a.mustBeLast, a = _a.a; + var a = _a.a; } function generic(t) { var x = t.x, rest = __rest(t, ["x"]); diff --git a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.js b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.js index 981ee08e20c..6be02246e39 100644 --- a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.js +++ b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.js @@ -176,63 +176,63 @@ function foo({a = 4, b = { x: 5 }}) { }); (function () { var y; - (_a = { y: 1 }, _b = _a.y, y = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = { y: 1 }.y, y = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y; - (_a = { y: 1 }, _b = _a.y, y = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = { y: 1 }.y, y = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y0; - (_a = { y0: 1 }, _b = _a.y0, y0 = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = { y0: 1 }.y0, y0 = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y0; - (_a = { y0: 1 }, _b = _a.y0, y0 = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = { y0: 1 }.y0, y0 = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y1; - (_a = {}, _b = _a.y1, y1 = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = {}.y1, y1 = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y1; - (_a = {}, _b = _a.y1, y1 = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = {}.y1, y1 = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y2, y3; - (_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c, _a); + (_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c); var _a, _b, _c; }); (function () { var y2, y3; - (_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c, _a); + (_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c); var _a, _b, _c; }); (function () { var y4, y5; - (_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c, _a); + (_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c); var _a, _b, _c; }); (function () { var y4, y5; - (_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c, _a); + (_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c); var _a, _b, _c; }); (function () { var z; - (_a = { z: { x: 1 } }, _b = _a.z, z = _b === void 0 ? { x: 5 } : _b, _a); - var _a, _b; + (_a = { z: { x: 1 } }.z, z = _a === void 0 ? { x: 5 } : _a); + var _a; }); (function () { var z; - (_a = { z: { x: 1 } }, _b = _a.z, z = _b === void 0 ? { x: 5 } : _b, _a); - var _a, _b; + (_a = { z: { x: 1 } }.z, z = _a === void 0 ? { x: 5 } : _a); + var _a; }); (function () { var a = { s: s }; From 08f467bd4cfd25393ee9993f536dc32a2ae0a6c6 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 14 Nov 2016 14:20:25 -0800 Subject: [PATCH 06/26] Clean up destructuring --- src/compiler/binder.ts | 39 +- src/compiler/factory.ts | 1094 ++++++++++++-------- src/compiler/transformers/destructuring.ts | 491 ++------- src/compiler/transformers/es2015.ts | 9 +- src/compiler/transformers/esnext.ts | 10 +- src/compiler/transformers/module/module.ts | 2 +- src/compiler/transformers/module/system.ts | 4 +- src/compiler/transformers/ts.ts | 2 +- src/compiler/types.ts | 64 +- src/compiler/utilities.ts | 72 ++ 10 files changed, 914 insertions(+), 873 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 9a477f05211..52131a90c3a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1280,7 +1280,7 @@ namespace ts { function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) { const name = !isOmittedExpression(node) ? node.name : undefined; if (isBindingPattern(name)) { - for (const child of name.elements) { + for (const child of name.elements) { bindInitializedVariableFlow(child); } } @@ -2629,7 +2629,7 @@ namespace ts { } // parameters with object rest destructuring are ES Next syntax - if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { + if (subtreeFlags & TransformFlags.ContainsObjectRest) { transformFlags |= TransformFlags.AssertESNext; } @@ -2867,7 +2867,7 @@ namespace ts { } // function declarations with object rest destructuring are ES Next syntax - if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { + if (subtreeFlags & TransformFlags.ContainsObjectRest) { transformFlags |= TransformFlags.AssertESNext; } @@ -2909,7 +2909,7 @@ namespace ts { } // function expressions with object rest destructuring are ES Next syntax - if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { + if (subtreeFlags & TransformFlags.ContainsObjectRest) { transformFlags |= TransformFlags.AssertESNext; } @@ -2952,7 +2952,7 @@ namespace ts { } // arrow functions with object rest destructuring are ES Next syntax - if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { + if (subtreeFlags & TransformFlags.ContainsObjectRest) { transformFlags |= TransformFlags.AssertESNext; } @@ -2982,16 +2982,11 @@ namespace ts { function computeVariableDeclaration(node: VariableDeclaration, subtreeFlags: TransformFlags) { let transformFlags = subtreeFlags; - const nameKind = node.name.kind; + transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; - // A VariableDeclaration with an object binding pattern is ES2015 syntax - // and possibly ESNext syntax if it contains an object binding pattern - if (nameKind === SyntaxKind.ObjectBindingPattern) { - transformFlags |= TransformFlags.AssertESNext | TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; - } - // A VariableDeclaration with an object binding pattern is ES2015 syntax. - else if (nameKind === SyntaxKind.ArrayBindingPattern) { - transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; + // A VariableDeclaration containing ObjectRest is ESNext syntax + if (subtreeFlags & TransformFlags.ContainsObjectRest) { + transformFlags |= TransformFlags.AssertESNext; } // Type annotations are TypeScript syntax. @@ -3213,13 +3208,6 @@ namespace ts { transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsSpread | TransformFlags.ContainsObjectSpread; break; - case SyntaxKind.BindingElement: - transformFlags |= TransformFlags.AssertES2015; - if ((node).dotDotDotToken) { - transformFlags |= TransformFlags.ContainsRest; - } - break; - case SyntaxKind.SuperKeyword: // This node is ES6 syntax. transformFlags |= TransformFlags.AssertES2015; @@ -3232,7 +3220,7 @@ namespace ts { case SyntaxKind.ObjectBindingPattern: transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; - if (subtreeFlags & TransformFlags.ContainsSpread) { + if (subtreeFlags & TransformFlags.ContainsRest) { transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectRest; } excludeFlags = TransformFlags.BindingPatternExcludes; @@ -3243,6 +3231,13 @@ namespace ts { excludeFlags = TransformFlags.BindingPatternExcludes; break; + case SyntaxKind.BindingElement: + transformFlags |= TransformFlags.AssertES2015; + if ((node).dotDotDotToken) { + transformFlags |= TransformFlags.ContainsRest; + } + break; + case SyntaxKind.Decorator: // This node is TypeScript syntax, and marks its container as also being TypeScript syntax. transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsDecorators; diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index ac3d5ed6e29..c107263b9d8 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1405,7 +1405,7 @@ namespace ts { return node; } - export function updateShorthandPropertyAssignment(node: ShorthandPropertyAssignment, name: Identifier, objectAssignmentInitializer: Expression) { + export function updateShorthandPropertyAssignment(node: ShorthandPropertyAssignment, name: Identifier, objectAssignmentInitializer: Expression) { if (node.name !== name || node.objectAssignmentInitializer !== objectAssignmentInitializer) { return updateNode(createShorthandPropertyAssignment(name, objectAssignmentInitializer, node), node); } @@ -1419,7 +1419,7 @@ namespace ts { return node; } - // Top-level nodes + // Top-level nodes export function updateSourceFileNode(node: SourceFile, statements: Statement[]) { if (node.statements !== statements) { @@ -1994,6 +1994,8 @@ namespace ts { ); } + // Utilities + export interface CallBinding { target: LeftHandSideExpression; thisArg: Expression; @@ -2339,8 +2341,6 @@ namespace ts { return qualifiedName; } - // Utilities - export function convertToFunctionBody(node: ConciseBody) { if (isBlock(node)) { return node; @@ -2424,407 +2424,6 @@ namespace ts { return node; } - /** - * Wraps the operand to a BinaryExpression in parentheses if they are needed to preserve the intended - * order of operations. - * - * @param binaryOperator The operator for the BinaryExpression. - * @param operand The operand for the BinaryExpression. - * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the - * BinaryExpression. - */ - export function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand?: Expression) { - const skipped = skipPartiallyEmittedExpressions(operand); - - // If the resulting expression is already parenthesized, we do not need to do any further processing. - if (skipped.kind === SyntaxKind.ParenthesizedExpression) { - return operand; - } - - return binaryOperandNeedsParentheses(binaryOperator, operand, isLeftSideOfBinary, leftOperand) - ? createParen(operand) - : operand; - } - - /** - * Determines whether the operand to a BinaryExpression needs to be parenthesized. - * - * @param binaryOperator The operator for the BinaryExpression. - * @param operand The operand for the BinaryExpression. - * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the - * BinaryExpression. - */ - function binaryOperandNeedsParentheses(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand: Expression) { - // If the operand has lower precedence, then it needs to be parenthesized to preserve the - // intent of the expression. For example, if the operand is `a + b` and the operator is - // `*`, then we need to parenthesize the operand to preserve the intended order of - // operations: `(a + b) * x`. - // - // If the operand has higher precedence, then it does not need to be parenthesized. For - // example, if the operand is `a * b` and the operator is `+`, then we do not need to - // parenthesize to preserve the intended order of operations: `a * b + x`. - // - // If the operand has the same precedence, then we need to check the associativity of - // the operator based on whether this is the left or right operand of the expression. - // - // For example, if `a / d` is on the right of operator `*`, we need to parenthesize - // to preserve the intended order of operations: `x * (a / d)` - // - // If `a ** d` is on the left of operator `**`, we need to parenthesize to preserve - // the intended order of operations: `(a ** b) ** c` - const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator); - const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator); - const emittedOperand = skipPartiallyEmittedExpressions(operand); - const operandPrecedence = getExpressionPrecedence(emittedOperand); - switch (compareValues(operandPrecedence, binaryOperatorPrecedence)) { - case Comparison.LessThan: - // If the operand is the right side of a right-associative binary operation - // and is a yield expression, then we do not need parentheses. - if (!isLeftSideOfBinary - && binaryOperatorAssociativity === Associativity.Right - && operand.kind === SyntaxKind.YieldExpression) { - return false; - } - - return true; - - case Comparison.GreaterThan: - return false; - - case Comparison.EqualTo: - if (isLeftSideOfBinary) { - // No need to parenthesize the left operand when the binary operator is - // left associative: - // (a*b)/x -> a*b/x - // (a**b)/x -> a**b/x - // - // Parentheses are needed for the left operand when the binary operator is - // right associative: - // (a/b)**x -> (a/b)**x - // (a**b)**x -> (a**b)**x - return binaryOperatorAssociativity === Associativity.Right; - } - else { - if (isBinaryExpression(emittedOperand) - && emittedOperand.operatorToken.kind === binaryOperator) { - // No need to parenthesize the right operand when the binary operator and - // operand are the same and one of the following: - // x*(a*b) => x*a*b - // x|(a|b) => x|a|b - // x&(a&b) => x&a&b - // x^(a^b) => x^a^b - if (operatorHasAssociativeProperty(binaryOperator)) { - return false; - } - - // No need to parenthesize the right operand when the binary operator - // is plus (+) if both the left and right operands consist solely of either - // literals of the same kind or binary plus (+) expressions for literals of - // the same kind (recursively). - // "a"+(1+2) => "a"+(1+2) - // "a"+("b"+"c") => "a"+"b"+"c" - if (binaryOperator === SyntaxKind.PlusToken) { - const leftKind = leftOperand ? getLiteralKindOfBinaryPlusOperand(leftOperand) : SyntaxKind.Unknown; - if (isLiteralKind(leftKind) && leftKind === getLiteralKindOfBinaryPlusOperand(emittedOperand)) { - return false; - } - } - } - - // No need to parenthesize the right operand when the operand is right - // associative: - // x/(a**b) -> x/a**b - // x**(a**b) -> x**a**b - // - // Parentheses are needed for the right operand when the operand is left - // associative: - // x/(a*b) -> x/(a*b) - // x**(a/b) -> x**(a/b) - const operandAssociativity = getExpressionAssociativity(emittedOperand); - return operandAssociativity === Associativity.Left; - } - } - } - - /** - * Determines whether a binary operator is mathematically associative. - * - * @param binaryOperator The binary operator. - */ - function operatorHasAssociativeProperty(binaryOperator: SyntaxKind) { - // The following operators are associative in JavaScript: - // (a*b)*c -> a*(b*c) -> a*b*c - // (a|b)|c -> a|(b|c) -> a|b|c - // (a&b)&c -> a&(b&c) -> a&b&c - // (a^b)^c -> a^(b^c) -> a^b^c - // - // While addition is associative in mathematics, JavaScript's `+` is not - // guaranteed to be associative as it is overloaded with string concatenation. - return binaryOperator === SyntaxKind.AsteriskToken - || binaryOperator === SyntaxKind.BarToken - || binaryOperator === SyntaxKind.AmpersandToken - || binaryOperator === SyntaxKind.CaretToken; - } - - interface BinaryPlusExpression extends BinaryExpression { - cachedLiteralKind: SyntaxKind; - } - - /** - * This function determines whether an expression consists of a homogeneous set of - * literal expressions or binary plus expressions that all share the same literal kind. - * It is used to determine whether the right-hand operand of a binary plus expression can be - * emitted without parentheses. - */ - function getLiteralKindOfBinaryPlusOperand(node: Expression): SyntaxKind { - node = skipPartiallyEmittedExpressions(node); - - if (isLiteralKind(node.kind)) { - return node.kind; - } - - if (node.kind === SyntaxKind.BinaryExpression && (node).operatorToken.kind === SyntaxKind.PlusToken) { - if ((node).cachedLiteralKind !== undefined) { - return (node).cachedLiteralKind; - } - - const leftKind = getLiteralKindOfBinaryPlusOperand((node).left); - const literalKind = isLiteralKind(leftKind) - && leftKind === getLiteralKindOfBinaryPlusOperand((node).right) - ? leftKind - : SyntaxKind.Unknown; - - (node).cachedLiteralKind = literalKind; - return literalKind; - } - - return SyntaxKind.Unknown; - } - - /** - * 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 emittedExpression = skipPartiallyEmittedExpressions(expression); - switch (emittedExpression.kind) { - case SyntaxKind.CallExpression: - return createParen(expression); - - case SyntaxKind.NewExpression: - return (emittedExpression).arguments - ? expression - : createParen(expression); - } - - return parenthesizeForAccess(expression); - } - - /** - * Wraps an expression in parentheses if it is needed in order to use the expression for - * property or element access. - * - * @param expr The expression node. - */ - 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: - // - // NewExpression: - // new C.x -> not the same as (new C).x - // NumericLiteral - // 1.x -> not the same as (1).x - // - const emittedExpression = skipPartiallyEmittedExpressions(expression); - if (isLeftHandSideExpression(emittedExpression) - && (emittedExpression.kind !== SyntaxKind.NewExpression || (emittedExpression).arguments) - && emittedExpression.kind !== SyntaxKind.NumericLiteral) { - return expression; - } - - return createParen(expression, /*location*/ expression); - } - - 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); - } - - function parenthesizeListElements(elements: NodeArray) { - let result: Expression[]; - for (let i = 0; i < elements.length; i++) { - const element = parenthesizeExpressionForList(elements[i]); - if (result !== undefined || element !== elements[i]) { - if (result === undefined) { - result = elements.slice(0, i); - } - - result.push(element); - } - } - - if (result !== undefined) { - return createNodeArray(result, elements, elements.hasTrailingComma); - } - - return elements; - } - - export function parenthesizeExpressionForList(expression: Expression) { - const emittedExpression = skipPartiallyEmittedExpressions(expression); - const expressionPrecedence = getExpressionPrecedence(emittedExpression); - const commaPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, SyntaxKind.CommaToken); - return expressionPrecedence > commaPrecedence - ? expression - : createParen(expression, /*location*/ expression); - } - - export function parenthesizeExpressionForExpressionStatement(expression: Expression) { - const emittedExpression = skipPartiallyEmittedExpressions(expression); - if (isCallExpression(emittedExpression)) { - const callee = emittedExpression.expression; - const kind = skipPartiallyEmittedExpressions(callee).kind; - if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) { - const mutableCall = getMutableClone(emittedExpression); - mutableCall.expression = createParen(callee, /*location*/ callee); - return recreatePartiallyEmittedExpressions(expression, mutableCall); - } - } - else { - const leftmostExpressionKind = getLeftmostExpression(emittedExpression).kind; - if (leftmostExpressionKind === SyntaxKind.ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind.FunctionExpression) { - return createParen(expression, /*location*/ expression); - } - } - - return expression; - } - - /** - * Clones a series of not-emitted expressions with a new inner expression. - * - * @param originalOuterExpression The original outer expression. - * @param newInnerExpression The new inner expression. - */ - function recreatePartiallyEmittedExpressions(originalOuterExpression: Expression, newInnerExpression: Expression) { - if (isPartiallyEmittedExpression(originalOuterExpression)) { - const clone = getMutableClone(originalOuterExpression); - clone.expression = recreatePartiallyEmittedExpressions(clone.expression, newInnerExpression); - return clone; - } - - return newInnerExpression; - } - - 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; - - case SyntaxKind.PartiallyEmittedExpression: - node = (node).expression; - continue; - } - - return node; - } - } - - export function parenthesizeConciseBody(body: ConciseBody): ConciseBody { - const emittedBody = skipPartiallyEmittedExpressions(body); - if (emittedBody.kind === SyntaxKind.ObjectLiteralExpression) { - return createParen(body, /*location*/ body); - } - - return body; - } - - export const enum OuterExpressionKinds { - Parentheses = 1 << 0, - Assertions = 1 << 1, - PartiallyEmittedExpressions = 1 << 2, - - All = Parentheses | Assertions | PartiallyEmittedExpressions - } - - export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; - export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; - export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { - let previousNode: Node; - do { - previousNode = node; - if (kinds & OuterExpressionKinds.Parentheses) { - node = skipParentheses(node); - } - - if (kinds & OuterExpressionKinds.Assertions) { - node = skipAssertions(node); - } - - if (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) { - node = skipPartiallyEmittedExpressions(node); - } - } - while (previousNode !== node); - - return node; - } - - export function skipParentheses(node: Expression): Expression; - export function skipParentheses(node: Node): Node; - export function skipParentheses(node: Node): Node { - while (node.kind === SyntaxKind.ParenthesizedExpression) { - node = (node).expression; - } - - return node; - } - - export function skipAssertions(node: Expression): Expression; - export function skipAssertions(node: Node): Node; - export function skipAssertions(node: Node): Node { - while (isAssertionExpression(node)) { - node = (node).expression; - } - - return node; - } - - export function skipPartiallyEmittedExpressions(node: Expression): Expression; - export function skipPartiallyEmittedExpressions(node: Node): Node; - export function skipPartiallyEmittedExpressions(node: Node) { - while (node.kind === SyntaxKind.PartiallyEmittedExpression) { - node = (node).expression; - } - - return node; - } - export function startOnNewLine(node: T): T { node.startsOnNewLine = true; return node; @@ -3295,7 +2894,7 @@ namespace ts { createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList( - flattenDestructuringToDeclarations( + flattenDestructuringBinding( context, parameter, temp, @@ -3454,9 +3053,6 @@ namespace ts { statements.push(forStatement); } - - - export function convertForOf(node: ForOfStatement, convertedLoopBodyStatements: Statement[], visitor: (node: Node) => VisitResult, enableSubstitutionsForBlockScopedBindings: () => void, @@ -3509,7 +3105,7 @@ namespace ts { if (firstOriginalDeclaration && isBindingPattern(firstOriginalDeclaration.name)) { // This works whether the declaration is a var, let, or const. // It will use rhsIterationValue _a[_i] as the initializer. - const declarations = flattenDestructuringToDeclarations( + const declarations = flattenDestructuringBinding( context, firstOriginalDeclaration, elementAccess, @@ -3566,7 +3162,7 @@ namespace ts { // This is a destructuring pattern, so we flatten the destructuring instead. statements.push( createStatement( - flattenDestructuringToExpression( + flattenDestructuringAssignment( context, assignment, /*needsValue*/ false, @@ -3651,4 +3247,680 @@ namespace ts { setEmitFlags(forStatement, EmitFlags.NoTokenTrailingSourceMaps); return forStatement; } + + /** + * Gets the initializer of an BindingOrAssignmentElement. + */ + export function getInitializerOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Expression | undefined { + if (isDeclarationBindingElement(bindingElement)) { + // `1` in `let { a = 1 } = ...` + // `1` in `let { a: b = 1 } = ...` + // `1` in `let { a: {b} = 1 } = ...` + // `1` in `let { a: [b] = 1 } = ...` + // `1` in `let [a = 1] = ...` + // `1` in `let [{a} = 1] = ...` + // `1` in `let [[a] = 1] = ...` + return bindingElement.initializer; + } + + if (isPropertyAssignment(bindingElement)) { + // `1` in `({ a: b = 1 } = ...)` + // `1` in `({ a: {b} = 1 } = ...)` + // `1` in `({ a: [b] = 1 } = ...)` + return isAssignmentExpression(bindingElement.initializer, /*excludeCompoundAssignment*/ true) + ? bindingElement.initializer.right + : undefined; + } + + if (isShorthandPropertyAssignment(bindingElement)) { + // `1` in `({ a = 1 } = ...)` + return bindingElement.objectAssignmentInitializer; + } + + if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { + // `1` in `[a = 1] = ...` + // `1` in `[{a} = 1] = ...` + // `1` in `[[a] = 1] = ...` + return bindingElement.right; + } + + if (isSpreadExpression(bindingElement) && isBindingOrAssignmentElement(bindingElement.expression)) { + // Recovery consistent with existing emit. + return getInitializerOfBindingOrAssignmentElement(bindingElement.expression); + } + } + + /** + * Gets the name of an BindingOrAssignmentElement. + */ + export function getTargetOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementTarget { + if (isDeclarationBindingElement(bindingElement)) { + // `a` in `let { a } = ...` + // `a` in `let { a = 1 } = ...` + // `b` in `let { a: b } = ...` + // `b` in `let { a: b = 1 } = ...` + // `a` in `let { ...a } = ...` + // `{b}` in `let { a: {b} } = ...` + // `{b}` in `let { a: {b} = 1 } = ...` + // `[b]` in `let { a: [b] } = ...` + // `[b]` in `let { a: [b] = 1 } = ...` + // `a` in `let [a] = ...` + // `a` in `let [a = 1] = ...` + // `a` in `let [...a] = ...` + // `{a}` in `let [{a}] = ...` + // `{a}` in `let [{a} = 1] = ...` + // `[a]` in `let [[a]] = ...` + // `[a]` in `let [[a] = 1] = ...` + return bindingElement.name; + } + + if (isObjectLiteralElementLike(bindingElement)) { + switch (bindingElement.kind) { + case SyntaxKind.PropertyAssignment: + // `b` in `({ a: b } = ...)` + // `b` in `({ a: b = 1 } = ...)` + // `{b}` in `({ a: {b} } = ...)` + // `{b}` in `({ a: {b} = 1 } = ...)` + // `[b]` in `({ a: [b] } = ...)` + // `[b]` in `({ a: [b] = 1 } = ...)` + // `b.c` in `({ a: b.c } = ...)` + // `b.c` in `({ a: b.c = 1 } = ...)` + // `b[0]` in `({ a: b[0] } = ...)` + // `b[0]` in `({ a: b[0] = 1 } = ...)` + return isBindingOrAssignmentElement(bindingElement.initializer) + ? getTargetOfBindingOrAssignmentElement(bindingElement.initializer) + : undefined; + + case SyntaxKind.ShorthandPropertyAssignment: + // `a` in `({ a } = ...)` + // `a` in `({ a = 1 } = ...)` + return bindingElement.name; + + case SyntaxKind.SpreadAssignment: + // `a` in `({ ...a } = ...)` + return isBindingOrAssignmentElement(bindingElement.expression) + ? getTargetOfBindingOrAssignmentElement(bindingElement.expression) + : undefined; + } + + // no target + return undefined; + } + + if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { + // `a` in `[a = 1] = ...` + // `{a}` in `[{a} = 1] = ...` + // `[a]` in `[[a] = 1] = ...` + // `a.b` in `[a.b = 1] = ...` + // `a[0]` in `[a[0] = 1] = ...` + return isBindingOrAssignmentElement(bindingElement.left) + ? getTargetOfBindingOrAssignmentElement(bindingElement.left) + : undefined; + } + + if (isSpreadExpression(bindingElement)) { + // `a` in `[...a] = ...` + return isBindingOrAssignmentElement(bindingElement.expression) + ? getTargetOfBindingOrAssignmentElement(bindingElement.expression) + : undefined; + } + + // `a` in `[a] = ...` + // `{a}` in `[{a}] = ...` + // `[a]` in `[[a]] = ...` + // `a.b` in `[a.b] = ...` + // `a[0]` in `[a[0]] = ...` + return bindingElement; + } + + /** + * Determines whether an BindingOrAssignmentElement is a rest element. + */ + export function getRestIndicatorOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementRestIndicator { + switch (bindingElement.kind) { + case SyntaxKind.Parameter: + case SyntaxKind.BindingElement: + // `...` in `let [...a] = ...` + return (bindingElement).dotDotDotToken; + + case SyntaxKind.SpreadElement: + case SyntaxKind.SpreadAssignment: + // `...` in `[...a] = ...` + return bindingElement; + } + + return undefined; + } + + /** + * Gets the property name of a BindingOrAssignmentElement + */ + export function getPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement) { + switch (bindingElement.kind) { + case SyntaxKind.BindingElement: + // `a` in `let { a: b } = ...` + // `[a]` in `let { [a]: b } = ...` + // `"a"` in `let { "a": b } = ...` + // `1` in `let { 1: b } = ...` + if ((bindingElement).propertyName) { + return (bindingElement).propertyName; + } + + break; + + case SyntaxKind.PropertyAssignment: + // `a` in `({ a: b } = ...)` + // `[a]` in `({ [a]: b } = ...)` + // `"a"` in `({ "a": b } = ...)` + // `1` in `({ 1: b } = ...)` + if ((bindingElement).name) { + return (bindingElement).name; + } + + break; + + case SyntaxKind.SpreadAssignment: + // `a` in `({ ...a } = ...)` + return (bindingElement).name; + } + + const target = getTargetOfBindingOrAssignmentElement(bindingElement); + if (target && isPropertyName(target)) { + return target; + } + + Debug.fail("Invalid property name for binding element."); + } + + /** + * Gets the elements of a BindingOrAssignmentPattern + */ + export function getElementsOfBindingOrAssignmentPattern(name: BindingOrAssignmentPattern): BindingOrAssignmentElement[] { + switch (name.kind) { + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + case SyntaxKind.ArrayLiteralExpression: + // `a` in `{a}` + // `a` in `[a]` + return map(name.elements, convertToBindingOrAssignmentElement); + + case SyntaxKind.ObjectLiteralExpression: + // `a` in `{a}` + return filter(name.properties, isBindingOrAssignmentElement); + } + } + + function convertToBindingOrAssignmentElement(node: Node) { + return isBindingOrAssignmentElement(node) ? node : createOmittedExpression(node); + } + + export function convertToArrayAssignmentElement(element: BindingOrAssignmentElement) { + if (isBindingElement(element)) { + if (element.dotDotDotToken) { + Debug.assertNode(element.name, isIdentifier); + return setOriginalNode(createSpread(element.name, element), element); + } + const expression = convertToAssignmentElementTarget(element.name); + return element.initializer ? setOriginalNode(createAssignment(expression, element.initializer, element), element) : expression; + } + Debug.assertNode(element, isExpression); + return element; + } + + export function convertToObjectAssignmentElement(element: BindingOrAssignmentElement) { + if (isBindingElement(element)) { + if (element.dotDotDotToken) { + Debug.assertNode(element.name, isIdentifier); + return setOriginalNode(createSpreadAssignment(element.name, element), element); + } + if (element.propertyName) { + const expression = convertToAssignmentElementTarget(element.name); + return setOriginalNode(createPropertyAssignment(element.propertyName, element.initializer ? createAssignment(expression, element.initializer) : expression, element), element); + } + Debug.assertNode(element.name, isIdentifier); + return setOriginalNode(createShorthandPropertyAssignment(element.name, element.initializer, element), element); + } + Debug.assertNode(element, isObjectLiteralElementLike); + return element; + } + + export function convertToAssignmentPattern(node: BindingOrAssignmentPattern): AssignmentPattern { + switch (node.kind) { + case SyntaxKind.ArrayBindingPattern: + case SyntaxKind.ArrayLiteralExpression: + return convertToArrayAssignmentPattern(node); + + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ObjectLiteralExpression: + return convertToObjectAssignmentPattern(node); + } + } + + export function convertToObjectAssignmentPattern(node: ObjectBindingOrAssignmentPattern) { + if (isObjectBindingPattern(node)) { + return setOriginalNode(createObjectLiteral(map(node.elements, convertToObjectAssignmentElement), node), node); + } + Debug.assertNode(node, isObjectLiteralExpression); + return node; + } + + export function convertToArrayAssignmentPattern(node: ArrayBindingOrAssignmentPattern) { + if (isArrayBindingPattern(node)) { + return setOriginalNode(createArrayLiteral(map(node.elements, convertToArrayAssignmentElement), node), node); + } + Debug.assertNode(node, isArrayLiteralExpression); + return node; + } + + export function convertToAssignmentElementTarget(node: BindingOrAssignmentElementTarget): Expression { + if (isBindingPattern(node)) { + return convertToAssignmentPattern(node); + } + + Debug.assertNode(node, isExpression); + return node; + } + + // Parenthesizing + + /** + * Wraps the operand to a BinaryExpression in parentheses if they are needed to preserve the intended + * order of operations. + * + * @param binaryOperator The operator for the BinaryExpression. + * @param operand The operand for the BinaryExpression. + * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the + * BinaryExpression. + */ + export function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand?: Expression) { + const skipped = skipPartiallyEmittedExpressions(operand); + + // If the resulting expression is already parenthesized, we do not need to do any further processing. + if (skipped.kind === SyntaxKind.ParenthesizedExpression) { + return operand; + } + + return binaryOperandNeedsParentheses(binaryOperator, operand, isLeftSideOfBinary, leftOperand) + ? createParen(operand) + : operand; + } + + /** + * Determines whether the operand to a BinaryExpression needs to be parenthesized. + * + * @param binaryOperator The operator for the BinaryExpression. + * @param operand The operand for the BinaryExpression. + * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the + * BinaryExpression. + */ + function binaryOperandNeedsParentheses(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand: Expression) { + // If the operand has lower precedence, then it needs to be parenthesized to preserve the + // intent of the expression. For example, if the operand is `a + b` and the operator is + // `*`, then we need to parenthesize the operand to preserve the intended order of + // operations: `(a + b) * x`. + // + // If the operand has higher precedence, then it does not need to be parenthesized. For + // example, if the operand is `a * b` and the operator is `+`, then we do not need to + // parenthesize to preserve the intended order of operations: `a * b + x`. + // + // If the operand has the same precedence, then we need to check the associativity of + // the operator based on whether this is the left or right operand of the expression. + // + // For example, if `a / d` is on the right of operator `*`, we need to parenthesize + // to preserve the intended order of operations: `x * (a / d)` + // + // If `a ** d` is on the left of operator `**`, we need to parenthesize to preserve + // the intended order of operations: `(a ** b) ** c` + const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator); + const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator); + const emittedOperand = skipPartiallyEmittedExpressions(operand); + const operandPrecedence = getExpressionPrecedence(emittedOperand); + switch (compareValues(operandPrecedence, binaryOperatorPrecedence)) { + case Comparison.LessThan: + // If the operand is the right side of a right-associative binary operation + // and is a yield expression, then we do not need parentheses. + if (!isLeftSideOfBinary + && binaryOperatorAssociativity === Associativity.Right + && operand.kind === SyntaxKind.YieldExpression) { + return false; + } + + return true; + + case Comparison.GreaterThan: + return false; + + case Comparison.EqualTo: + if (isLeftSideOfBinary) { + // No need to parenthesize the left operand when the binary operator is + // left associative: + // (a*b)/x -> a*b/x + // (a**b)/x -> a**b/x + // + // Parentheses are needed for the left operand when the binary operator is + // right associative: + // (a/b)**x -> (a/b)**x + // (a**b)**x -> (a**b)**x + return binaryOperatorAssociativity === Associativity.Right; + } + else { + if (isBinaryExpression(emittedOperand) + && emittedOperand.operatorToken.kind === binaryOperator) { + // No need to parenthesize the right operand when the binary operator and + // operand are the same and one of the following: + // x*(a*b) => x*a*b + // x|(a|b) => x|a|b + // x&(a&b) => x&a&b + // x^(a^b) => x^a^b + if (operatorHasAssociativeProperty(binaryOperator)) { + return false; + } + + // No need to parenthesize the right operand when the binary operator + // is plus (+) if both the left and right operands consist solely of either + // literals of the same kind or binary plus (+) expressions for literals of + // the same kind (recursively). + // "a"+(1+2) => "a"+(1+2) + // "a"+("b"+"c") => "a"+"b"+"c" + if (binaryOperator === SyntaxKind.PlusToken) { + const leftKind = leftOperand ? getLiteralKindOfBinaryPlusOperand(leftOperand) : SyntaxKind.Unknown; + if (isLiteralKind(leftKind) && leftKind === getLiteralKindOfBinaryPlusOperand(emittedOperand)) { + return false; + } + } + } + + // No need to parenthesize the right operand when the operand is right + // associative: + // x/(a**b) -> x/a**b + // x**(a**b) -> x**a**b + // + // Parentheses are needed for the right operand when the operand is left + // associative: + // x/(a*b) -> x/(a*b) + // x**(a/b) -> x**(a/b) + const operandAssociativity = getExpressionAssociativity(emittedOperand); + return operandAssociativity === Associativity.Left; + } + } + } + + /** + * Determines whether a binary operator is mathematically associative. + * + * @param binaryOperator The binary operator. + */ + function operatorHasAssociativeProperty(binaryOperator: SyntaxKind) { + // The following operators are associative in JavaScript: + // (a*b)*c -> a*(b*c) -> a*b*c + // (a|b)|c -> a|(b|c) -> a|b|c + // (a&b)&c -> a&(b&c) -> a&b&c + // (a^b)^c -> a^(b^c) -> a^b^c + // + // While addition is associative in mathematics, JavaScript's `+` is not + // guaranteed to be associative as it is overloaded with string concatenation. + return binaryOperator === SyntaxKind.AsteriskToken + || binaryOperator === SyntaxKind.BarToken + || binaryOperator === SyntaxKind.AmpersandToken + || binaryOperator === SyntaxKind.CaretToken; + } + + interface BinaryPlusExpression extends BinaryExpression { + cachedLiteralKind: SyntaxKind; + } + + /** + * This function determines whether an expression consists of a homogeneous set of + * literal expressions or binary plus expressions that all share the same literal kind. + * It is used to determine whether the right-hand operand of a binary plus expression can be + * emitted without parentheses. + */ + function getLiteralKindOfBinaryPlusOperand(node: Expression): SyntaxKind { + node = skipPartiallyEmittedExpressions(node); + + if (isLiteralKind(node.kind)) { + return node.kind; + } + + if (node.kind === SyntaxKind.BinaryExpression && (node).operatorToken.kind === SyntaxKind.PlusToken) { + if ((node).cachedLiteralKind !== undefined) { + return (node).cachedLiteralKind; + } + + const leftKind = getLiteralKindOfBinaryPlusOperand((node).left); + const literalKind = isLiteralKind(leftKind) + && leftKind === getLiteralKindOfBinaryPlusOperand((node).right) + ? leftKind + : SyntaxKind.Unknown; + + (node).cachedLiteralKind = literalKind; + return literalKind; + } + + return SyntaxKind.Unknown; + } + + /** + * 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 emittedExpression = skipPartiallyEmittedExpressions(expression); + switch (emittedExpression.kind) { + case SyntaxKind.CallExpression: + return createParen(expression); + + case SyntaxKind.NewExpression: + return (emittedExpression).arguments + ? expression + : createParen(expression); + } + + return parenthesizeForAccess(expression); + } + + /** + * Wraps an expression in parentheses if it is needed in order to use the expression for + * property or element access. + * + * @param expr The expression node. + */ + 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: + // + // NewExpression: + // new C.x -> not the same as (new C).x + // NumericLiteral + // 1.x -> not the same as (1).x + // + const emittedExpression = skipPartiallyEmittedExpressions(expression); + if (isLeftHandSideExpression(emittedExpression) + && (emittedExpression.kind !== SyntaxKind.NewExpression || (emittedExpression).arguments) + && emittedExpression.kind !== SyntaxKind.NumericLiteral) { + return expression; + } + + return createParen(expression, /*location*/ expression); + } + + 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); + } + + function parenthesizeListElements(elements: NodeArray) { + let result: Expression[]; + for (let i = 0; i < elements.length; i++) { + const element = parenthesizeExpressionForList(elements[i]); + if (result !== undefined || element !== elements[i]) { + if (result === undefined) { + result = elements.slice(0, i); + } + + result.push(element); + } + } + + if (result !== undefined) { + return createNodeArray(result, elements, elements.hasTrailingComma); + } + + return elements; + } + + export function parenthesizeExpressionForList(expression: Expression) { + const emittedExpression = skipPartiallyEmittedExpressions(expression); + const expressionPrecedence = getExpressionPrecedence(emittedExpression); + const commaPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, SyntaxKind.CommaToken); + return expressionPrecedence > commaPrecedence + ? expression + : createParen(expression, /*location*/ expression); + } + + export function parenthesizeExpressionForExpressionStatement(expression: Expression) { + const emittedExpression = skipPartiallyEmittedExpressions(expression); + if (isCallExpression(emittedExpression)) { + const callee = emittedExpression.expression; + const kind = skipPartiallyEmittedExpressions(callee).kind; + if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) { + const mutableCall = getMutableClone(emittedExpression); + mutableCall.expression = createParen(callee, /*location*/ callee); + return recreatePartiallyEmittedExpressions(expression, mutableCall); + } + } + else { + const leftmostExpressionKind = getLeftmostExpression(emittedExpression).kind; + if (leftmostExpressionKind === SyntaxKind.ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind.FunctionExpression) { + return createParen(expression, /*location*/ expression); + } + } + + return expression; + } + + /** + * Clones a series of not-emitted expressions with a new inner expression. + * + * @param originalOuterExpression The original outer expression. + * @param newInnerExpression The new inner expression. + */ + function recreatePartiallyEmittedExpressions(originalOuterExpression: Expression, newInnerExpression: Expression) { + if (isPartiallyEmittedExpression(originalOuterExpression)) { + const clone = getMutableClone(originalOuterExpression); + clone.expression = recreatePartiallyEmittedExpressions(clone.expression, newInnerExpression); + return clone; + } + + return newInnerExpression; + } + + 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; + + case SyntaxKind.PartiallyEmittedExpression: + node = (node).expression; + continue; + } + + return node; + } + } + + export function parenthesizeConciseBody(body: ConciseBody): ConciseBody { + const emittedBody = skipPartiallyEmittedExpressions(body); + if (emittedBody.kind === SyntaxKind.ObjectLiteralExpression) { + return createParen(body, /*location*/ body); + } + + return body; + } + + export const enum OuterExpressionKinds { + Parentheses = 1 << 0, + Assertions = 1 << 1, + PartiallyEmittedExpressions = 1 << 2, + + All = Parentheses | Assertions | PartiallyEmittedExpressions + } + + export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; + export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; + export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { + let previousNode: Node; + do { + previousNode = node; + if (kinds & OuterExpressionKinds.Parentheses) { + node = skipParentheses(node); + } + + if (kinds & OuterExpressionKinds.Assertions) { + node = skipAssertions(node); + } + + if (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) { + node = skipPartiallyEmittedExpressions(node); + } + } + while (previousNode !== node); + + return node; + } + + export function skipParentheses(node: Expression): Expression; + export function skipParentheses(node: Node): Node; + export function skipParentheses(node: Node): Node { + while (node.kind === SyntaxKind.ParenthesizedExpression) { + node = (node).expression; + } + + return node; + } + + export function skipAssertions(node: Expression): Expression; + export function skipAssertions(node: Node): Node; + export function skipAssertions(node: Node): Node { + while (isAssertionExpression(node)) { + node = (node).expression; + } + + return node; + } + + export function skipPartiallyEmittedExpressions(node: Expression): Expression; + export function skipPartiallyEmittedExpressions(node: Node): Node; + export function skipPartiallyEmittedExpressions(node: Node) { + while (node.kind === SyntaxKind.PartiallyEmittedExpression) { + node = (node).expression; + } + + return node; + } } diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index d4886457062..b8da29ae291 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -3,80 +3,21 @@ /*@internal*/ namespace ts { - type EffectiveBindingOrAssignmentElement = VariableDeclaration | ParameterDeclaration | BindingElement | ObjectLiteralElementLike | Expression; - type EffectiveObjectBindingOrAssignmentPattern = ObjectBindingPattern | ObjectLiteralExpression; - type EffectiveArrayBindingOrAssignmentPattern = ArrayBindingPattern | ArrayLiteralExpression; - type EffectiveBindingOrAssignmentPattern = EffectiveObjectBindingOrAssignmentPattern | EffectiveArrayBindingOrAssignmentPattern; - type EffectiveBindingOrAssignmentTarget = EffectiveBindingOrAssignmentPattern | Expression; - type EffectiveBindingOrAssignmentRestIndicator = DotDotDotToken | SpreadElement | SpreadAssignment; - interface FlattenHost { context: TransformationContext; level: FlattenLevel; recordTempVariablesInLine: boolean; emitExpression: (value: Expression) => void; - emitBindingOrAssignment: (target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) => void; - createArrayBindingOrAssignmentPattern: (elements: EffectiveBindingOrAssignmentElement[]) => EffectiveArrayBindingOrAssignmentPattern; - createObjectBindingOrAssignmentPattern: (elements: EffectiveBindingOrAssignmentElement[]) => EffectiveObjectBindingOrAssignmentPattern; - createArrayBindingOrAssignmentElement: (node: Identifier) => EffectiveBindingOrAssignmentElement; + emitBindingOrAssignment: (target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node) => void; + createArrayBindingOrAssignmentPattern: (elements: BindingOrAssignmentElement[]) => ArrayBindingOrAssignmentPattern; + createObjectBindingOrAssignmentPattern: (elements: BindingOrAssignmentElement[]) => ObjectBindingOrAssignmentPattern; + createArrayBindingOrAssignmentElement: (node: Identifier) => BindingOrAssignmentElement; visitor?: (node: Node) => VisitResult; } export const enum FlattenLevel { - ObjectRest, All, - } - - export const enum FlattenOutput { - Expression, - Declarations - } - - export const enum FlattenOptions { - NeedsValue = 1 << 0, - SkipInitializer = 1 << 1, - RecordTempVariablesInLine = 1 << 2, - } - - export function flattenDestructuring( - output: FlattenOutput.Expression, - level: FlattenLevel, - node: VariableDeclaration | DestructuringAssignment, - visitor: (node: Node) => VisitResult, - context: TransformationContext, - options: FlattenOptions, - createAssignment?: (name: Identifier, value: Expression, location?: TextRange) => Expression): Expression; - export function flattenDestructuring( - output: FlattenOutput.Declarations, - level: FlattenLevel, - node: VariableDeclaration | ParameterDeclaration, - visitor: (node: Node) => VisitResult, - context: TransformationContext, - options: FlattenOptions, - boundValue?: Expression): VariableDeclaration[]; - export function flattenDestructuring( - _output: FlattenOutput, - _level: FlattenLevel, - _node: VariableDeclaration | ParameterDeclaration | DestructuringAssignment, - _visitor: (node: Node) => VisitResult, - _context: TransformationContext, - _options: FlattenOptions, - _valueOrCallback?: Expression | ((name: Identifier, value: Expression, location?: TextRange) => Expression)): Expression | VariableDeclaration[] { - // const flattenMode = flags & FlattenFlags.FlattenMask; - // const outputMode = flags & FlattenFlags.OutputMask; - // const options = flags & FlattenFlags.OptionsMask; - - - // if (outputMode === FlattenFlags.OutputExpressions) { - // return flattenDestructuringToExpression(context, node, !(options & FlattenFlags.NeedsValue), flattenMode, createAssignmentCallback, visitor); - // } - // else { - // return flattenDestructuringToDeclarations( - // context, - // node, - // boundValue) - // } - return; + ObjectRest, } /** @@ -90,7 +31,7 @@ namespace ts { * @param createAssignmentCallback A callback used to create the assignment expression. * @param visitor A visitor used to visit initializers. */ - export function flattenDestructuringToExpression( + export function flattenDestructuringAssignment( context: TransformationContext, node: VariableDeclaration | DestructuringAssignment, needsValue: boolean, @@ -120,9 +61,9 @@ namespace ts { recordTempVariablesInLine: false, emitExpression, emitBindingOrAssignment, - createArrayBindingOrAssignmentPattern: createEffectiveArrayAssignmentPattern, - createObjectBindingOrAssignmentPattern: createEffectiveObjectAssignmentPattern, - createArrayBindingOrAssignmentElement: createEffectiveAssignmentElement, + createArrayBindingOrAssignmentPattern: makeArrayAssignmentPattern, + createObjectBindingOrAssignmentPattern: makeObjectAssignmentPattern, + createArrayBindingOrAssignmentElement: makeAssignmentElement, visitor }; @@ -148,7 +89,7 @@ namespace ts { } } - flattenEffectiveBindingElement(host, node, value, location, /*skipInitializer*/ isDestructuringAssignment(node)); + flattenBindingOrAssignmentElement(host, node, value, location, /*skipInitializer*/ isDestructuringAssignment(node)); if (value && needsValue) { expressions.push(value); @@ -164,7 +105,7 @@ namespace ts { expressions.push(expression); } - function emitBindingOrAssignment(target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) { + function emitBindingOrAssignment(target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node) { Debug.assertNode(target, createAssignmentCallback ? isIdentifier : isExpression); const expression = createAssignmentCallback ? createAssignmentCallback(target, value, location) @@ -183,7 +124,7 @@ namespace ts { * @param recordTempVariablesInLine Indicates whether temporary variables should be recored in-line. * @param level Indicates the extent to which flattening should occur. */ - export function flattenDestructuringToDeclarations( + export function flattenDestructuringBinding( context: TransformationContext, node: VariableDeclaration | ParameterDeclaration, boundValue: Expression | undefined, @@ -201,13 +142,13 @@ namespace ts { recordTempVariablesInLine, emitExpression, emitBindingOrAssignment, - createArrayBindingOrAssignmentPattern: createEffectiveArrayBindingPattern, - createObjectBindingOrAssignmentPattern: createEffectiveObjectBindingPattern, - createArrayBindingOrAssignmentElement: createEffectiveBindingElement, + createArrayBindingOrAssignmentPattern: makeArrayBindingPattern, + createObjectBindingOrAssignmentPattern: makeObjectBindingPattern, + createArrayBindingOrAssignmentElement: makeBindingElement, visitor }; - flattenEffectiveBindingElement(host, node, boundValue, node, skipInitializer); + flattenBindingOrAssignmentElement(host, node, boundValue, node, skipInitializer); if (pendingExpressions) { const temp = createTempVariable(/*recordTempVariable*/ undefined); @@ -248,7 +189,7 @@ namespace ts { pendingExpressions = append(pendingExpressions, value); } - function emitBindingOrAssignment(target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) { + function emitBindingOrAssignment(target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node) { Debug.assertNode(target, isBindingName); if (pendingExpressions) { value = inlineExpressions(append(pendingExpressions, value)); @@ -258,14 +199,14 @@ namespace ts { } } - function flattenEffectiveBindingElement( + function flattenBindingOrAssignmentElement( host: FlattenHost, - bindingElement: EffectiveBindingOrAssignmentElement, + bindingElement: BindingOrAssignmentElement, boundValue: Expression | undefined, location: TextRange, skipInitializer?: boolean) { if (!skipInitializer) { - const initializer = visitNode(getInitializerOfEffectiveBindingElement(bindingElement), host.visitor, isExpression); + const initializer = visitNode(getInitializerOfBindingOrAssignmentElement(bindingElement), host.visitor, isExpression); if (initializer) { // Combine value and initializer boundValue = boundValue ? createDefaultValueCheck(host, boundValue, initializer, location) : initializer; @@ -275,39 +216,36 @@ namespace ts { boundValue = createVoidZero(); } } - const bindingTarget = getTargetOfEffectiveBindingElement(bindingElement); - if (!isEffectiveBindingPattern(bindingTarget)) { - host.emitBindingOrAssignment(bindingTarget, boundValue, location, /*original*/ bindingElement); + const bindingTarget = getTargetOfBindingOrAssignmentElement(bindingElement); + if (isObjectBindingOrAssignmentPattern(bindingTarget)) { + flattenObjectBindingOrAssignmentPattern(host, bindingElement, bindingTarget, boundValue, location); + } + else if (isArrayBindingOrAssignmentPattern(bindingTarget)) { + flattenArrayBindingOrAssignmentPattern(host, bindingElement, bindingTarget, boundValue, location); } else { - const elements = getElementsOfEffectiveBindingPattern(bindingTarget); - 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. - const reuseIdentifierExpressions = !isDeclarationBindingElement(bindingElement) || numElements !== 0; - boundValue = ensureIdentifier(host, boundValue, reuseIdentifierExpressions, location); - } - if (isEffectiveObjectBindingPattern(bindingTarget)) { - flattenEffectiveObjectBindingElements(host, bindingTarget, elements, boundValue, location); - } - else { - flattenEffectiveArrayBindingElements(host, bindingTarget, elements, boundValue, location); - } + host.emitBindingOrAssignment(bindingTarget, boundValue, location, /*original*/ bindingElement); } } - function flattenEffectiveObjectBindingElements(host: FlattenHost, bindingTarget: EffectiveObjectBindingOrAssignmentPattern, elements: EffectiveBindingOrAssignmentElement[], boundValue: Expression, location: TextRange) { - let bindingElements: EffectiveBindingOrAssignmentElement[]; + function flattenObjectBindingOrAssignmentPattern(host: FlattenHost, parentElement: BindingOrAssignmentElement, bindingTarget: ObjectBindingOrAssignmentPattern, boundValue: Expression, location: TextRange) { + const elements = getElementsOfBindingOrAssignmentPattern(bindingTarget); 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. + const reuseIdentifierExpressions = !isDeclarationBindingElement(parentElement) || numElements !== 0; + boundValue = ensureIdentifier(host, boundValue, reuseIdentifierExpressions, location); + } + let bindingElements: BindingOrAssignmentElement[]; for (let i = 0; i < numElements; i++) { const element = elements[i]; - if (!getEffectiveRestIndicator(element)) { - if (host.level <= FlattenLevel.ObjectRest + if (!getRestIndicatorOfBindingOrAssignmentElement(element)) { + if (host.level >= FlattenLevel.ObjectRest && !(element.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) - && !(getTargetOfEffectiveBindingElement(element).transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest))) { + && !(getTargetOfBindingOrAssignmentElement(element).transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest))) { bindingElements = append(bindingElements, element); } else { @@ -315,9 +253,9 @@ namespace ts { host.emitBindingOrAssignment(host.createObjectBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget); bindingElements = undefined; } - const propertyName = getEffectivePropertyNameOfEffectiveBindingElement(element); + const propertyName = getPropertyNameOfBindingOrAssignmentElement(element); const value = createDestructuringPropertyAccess(host, boundValue, propertyName); - flattenEffectiveBindingElement(host, element, value, /*location*/ element); + flattenBindingOrAssignmentElement(host, element, value, /*location*/ element); } } else if (i === numElements - 1) { @@ -326,7 +264,7 @@ namespace ts { bindingElements = undefined; } const value = createRestCall(boundValue, elements, bindingTarget); - flattenEffectiveBindingElement(host, element, value, element); + flattenBindingOrAssignmentElement(host, element, value, element); } } if (bindingElements) { @@ -334,16 +272,27 @@ namespace ts { } } - function flattenEffectiveArrayBindingElements(host: FlattenHost, bindingTarget: EffectiveArrayBindingOrAssignmentPattern, elements: EffectiveBindingOrAssignmentElement[], boundValue: Expression, location: TextRange) { - let bindingElements: EffectiveBindingOrAssignmentElement[]; - let spreadContainingElements: [Identifier, EffectiveBindingOrAssignmentElement][]; + function flattenArrayBindingOrAssignmentPattern(host: FlattenHost, parentElement: BindingOrAssignmentElement, bindingTarget: ArrayBindingOrAssignmentPattern, boundValue: Expression, location: TextRange) { + const elements = getElementsOfBindingOrAssignmentPattern(bindingTarget); const numElements = elements.length; + if (numElements !== 1 && (host.level < FlattenLevel.ObjectRest || numElements === 0)) { + // 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. + const reuseIdentifierExpressions = !isDeclarationBindingElement(parentElement) || numElements !== 0; + boundValue = ensureIdentifier(host, boundValue, reuseIdentifierExpressions, location); + } + let bindingElements: BindingOrAssignmentElement[]; + let restContainingElements: [Identifier, BindingOrAssignmentElement][]; for (let i = 0; i < numElements; i++) { const element = elements[i]; - if (host.level <= FlattenLevel.ObjectRest) { - if (element.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest) && i < numElements - 1) { + if (host.level >= FlattenLevel.ObjectRest) { + // If an array pattern contains an ObjectRest, we must cache the result so that we + // can perform the ObjectRest destructuring in a different declaration + if (element.transformFlags & TransformFlags.ContainsObjectRest) { const temp = createTempVariable(/*recordTempVariable*/ undefined); - spreadContainingElements = append(spreadContainingElements, <[Identifier, EffectiveBindingOrAssignmentElement]>[temp, element]); + restContainingElements = append(restContainingElements, <[Identifier, BindingOrAssignmentElement]>[temp, element]); bindingElements = append(bindingElements, host.createArrayBindingOrAssignmentElement(temp)); } else { @@ -353,21 +302,21 @@ namespace ts { else if (isOmittedExpression(element)) { continue; } - else if (!getEffectiveRestIndicator(element)) { + else if (!getRestIndicatorOfBindingOrAssignmentElement(element)) { const value = createElementAccess(boundValue, i); - flattenEffectiveBindingElement(host, element, value, /*location*/ element); + flattenBindingOrAssignmentElement(host, element, value, /*location*/ element); } else if (i === numElements - 1) { const value = createArraySlice(boundValue, i); - flattenEffectiveBindingElement(host, element, value, /*location*/ element); + flattenBindingOrAssignmentElement(host, element, value, /*location*/ element); } } if (bindingElements) { host.emitBindingOrAssignment(host.createArrayBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget); } - if (spreadContainingElements) { - for (const [id, element] of spreadContainingElements) { - flattenEffectiveBindingElement(host, element, id, element); + if (restContainingElements) { + for (const [id, element] of restContainingElements) { + flattenBindingOrAssignmentElement(host, element, id, element); } } } @@ -448,321 +397,35 @@ namespace ts { } } - /** - * Determines whether the EffectiveBindingElement is a declaration - */ - function isDeclarationBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement { - switch (bindingElement.kind) { - case SyntaxKind.VariableDeclaration: - case SyntaxKind.Parameter: - case SyntaxKind.BindingElement: - return true; - } - - return false; - } - - /** - * Gets the initializer of an EffectiveBindingElement. - */ - function getInitializerOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): Expression | undefined { - if (isDeclarationBindingElement(bindingElement)) { - // `1` in `let { a = 1 } = ...` - // `1` in `let { a: b = 1 } = ...` - // `1` in `let { a: {b} = 1 } = ...` - // `1` in `let { a: [b] = 1 } = ...` - // `1` in `let [a = 1] = ...` - // `1` in `let [{a} = 1] = ...` - // `1` in `let [[a] = 1] = ...` - return bindingElement.initializer; - } - - if (isPropertyAssignment(bindingElement)) { - // `1` in `({ a: b = 1 } = ...)` - // `1` in `({ a: {b} = 1 } = ...)` - // `1` in `({ a: [b] = 1 } = ...)` - return isAssignmentExpression(bindingElement.initializer, /*excludeCompoundAssignment*/ true) - ? bindingElement.initializer.right - : undefined; - } - - if (isShorthandPropertyAssignment(bindingElement)) { - // `1` in `({ a = 1 } = ...)` - return bindingElement.objectAssignmentInitializer; - } - - if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { - // `1` in `[a = 1] = ...` - // `1` in `[{a} = 1] = ...` - // `1` in `[[a] = 1] = ...` - return bindingElement.right; - } - - if (isSpreadExpression(bindingElement) || isPartiallyEmittedExpression(bindingElement)) { - // Recovery consistent with existing emit. - return getInitializerOfEffectiveBindingElement(bindingElement.expression); - } - } - - /** - * Gets the name of an EffectiveBindingElement. - */ - function getTargetOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): EffectiveBindingOrAssignmentTarget { - if (isDeclarationBindingElement(bindingElement)) { - // `a` in `let { a } = ...` - // `a` in `let { a = 1 } = ...` - // `b` in `let { a: b } = ...` - // `b` in `let { a: b = 1 } = ...` - // `a` in `let { ...a } = ...` - // `{b}` in `let { a: {b} } = ...` - // `{b}` in `let { a: {b} = 1 } = ...` - // `[b]` in `let { a: [b] } = ...` - // `[b]` in `let { a: [b] = 1 } = ...` - // `a` in `let [a] = ...` - // `a` in `let [a = 1] = ...` - // `a` in `let [...a] = ...` - // `{a}` in `let [{a}] = ...` - // `{a}` in `let [{a} = 1] = ...` - // `[a]` in `let [[a]] = ...` - // `[a]` in `let [[a] = 1] = ...` - return bindingElement.name; - } - - if (isObjectLiteralElementLike(bindingElement)) { - switch (bindingElement.kind) { - case SyntaxKind.PropertyAssignment: - // `b` in `({ a: b } = ...)` - // `b` in `({ a: b = 1 } = ...)` - // `{b}` in `({ a: {b} } = ...)` - // `{b}` in `({ a: {b} = 1 } = ...)` - // `[b]` in `({ a: [b] } = ...)` - // `[b]` in `({ a: [b] = 1 } = ...)` - // `b.c` in `({ a: b.c } = ...)` - // `b.c` in `({ a: b.c = 1 } = ...)` - // `b[0]` in `({ a: b[0] } = ...)` - // `b[0]` in `({ a: b[0] = 1 } = ...)` - return getTargetOfEffectiveBindingElement(bindingElement.initializer); - - case SyntaxKind.ShorthandPropertyAssignment: - // `a` in `({ a } = ...)` - // `a` in `({ a = 1 } = ...)` - return bindingElement.name; - - case SyntaxKind.SpreadAssignment: - // `a` in `({ ...a } = ...)` - return getTargetOfEffectiveBindingElement(bindingElement.expression); - } - - // no target - return undefined; - } - - if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { - // `a` in `[a = 1] = ...` - // `{a}` in `[{a} = 1] = ...` - // `[a]` in `[[a] = 1] = ...` - // `a.b` in `[a.b = 1] = ...` - // `a[0]` in `[a[0] = 1] = ...` - return getTargetOfEffectiveBindingElement(bindingElement.left); - } - - if (isSpreadExpression(bindingElement) || isPartiallyEmittedExpression(bindingElement)) { - // `a` in `[...a] = ...` - return getTargetOfEffectiveBindingElement(bindingElement.expression); - } - - // `a` in `[a] = ...` - // `{a}` in `[{a}] = ...` - // `[a]` in `[[a]] = ...` - // `a.b` in `[a.b] = ...` - // `a[0]` in `[a[0]] = ...` - return bindingElement; - } - - /** - * Determines whether an EffectiveBindingElement is a rest element. - */ - function getEffectiveRestIndicator(bindingElement: EffectiveBindingOrAssignmentElement): EffectiveBindingOrAssignmentRestIndicator { - switch (bindingElement.kind) { - case SyntaxKind.Parameter: - case SyntaxKind.BindingElement: - // `...` in `let [...a] = ...` - return (bindingElement).dotDotDotToken; - - case SyntaxKind.SpreadElement: - case SyntaxKind.SpreadAssignment: - // `...` in `[...a] = ...` - return bindingElement; - } - - return undefined; - } - - /** - * Gets the property name of a BindingElement-like element - */ - function getEffectivePropertyNameOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement) { - switch (bindingElement.kind) { - case SyntaxKind.BindingElement: - // `a` in `let { a: b } = ...` - // `[a]` in `let { [a]: b } = ...` - // `"a"` in `let { "a": b } = ...` - // `1` in `let { 1: b } = ...` - if ((bindingElement).propertyName) { - return (bindingElement).propertyName; - } - - break; - - case SyntaxKind.PropertyAssignment: - // `a` in `({ a: b } = ...)` - // `[a]` in `({ [a]: b } = ...)` - // `"a"` in `({ "a": b } = ...)` - // `1` in `({ 1: b } = ...)` - if ((bindingElement).name) { - return (bindingElement).name; - } - - break; - - case SyntaxKind.SpreadAssignment: - // `a` in `({ ...a } = ...)` - return (bindingElement).name; - } - - const target = getTargetOfEffectiveBindingElement(bindingElement); - if (target && isPropertyName(target)) { - return target; - } - - Debug.fail("Invalid property name for binding element."); - } - - /** - * Determines whether a node is BindingPattern-like - */ - function isEffectiveBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveBindingOrAssignmentPattern { - return isEffectiveObjectBindingPattern(node) - || isEffectiveArrayBindingPattern(node); - } - - /** - * Determines whether a node is ObjectBindingPattern-like - */ - function isEffectiveObjectBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveObjectBindingOrAssignmentPattern { - switch (node.kind) { - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ObjectLiteralExpression: - return true; - } - - return false; - } - - /** - * Determines whether a node is ArrayBindingPattern-like - */ - function isEffectiveArrayBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveArrayBindingOrAssignmentPattern { - switch (node.kind) { - case SyntaxKind.ArrayBindingPattern: - case SyntaxKind.ArrayLiteralExpression: - return true; - } - - return false; - } - - /** - * Gets the elements of a BindingPattern-like name - */ - function getElementsOfEffectiveBindingPattern(name: EffectiveBindingOrAssignmentPattern): EffectiveBindingOrAssignmentElement[] { - switch (name.kind) { - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ArrayBindingPattern: - case SyntaxKind.ArrayLiteralExpression: - // `a` in `{a}` - // `a` in `[a]` - return name.elements; - - case SyntaxKind.ObjectLiteralExpression: - // `a` in `{a}` - return name.properties; - } - } - - function createEffectiveArrayBindingPattern(elements: EffectiveBindingOrAssignmentElement[]) { + function makeArrayBindingPattern(elements: BindingOrAssignmentElement[]) { Debug.assertEachNode(elements, isArrayBindingElement); return createArrayBindingPattern(elements); } - function createEffectiveArrayAssignmentPattern(elements: EffectiveBindingOrAssignmentElement[]) { - return createArrayLiteral(map(elements, convertToArrayLiteralElement)); + function makeArrayAssignmentPattern(elements: BindingOrAssignmentElement[]) { + return createArrayLiteral(map(elements, convertToArrayAssignmentElement)); } - function createEffectiveObjectBindingPattern(elements: EffectiveBindingOrAssignmentElement[]) { + function makeObjectBindingPattern(elements: BindingOrAssignmentElement[]) { Debug.assertEachNode(elements, isBindingElement); return createObjectBindingPattern(elements); } - function createEffectiveObjectAssignmentPattern(elements: EffectiveBindingOrAssignmentElement[]) { - return createObjectLiteral(map(elements, convertToObjectLiteralElement)); + function makeObjectAssignmentPattern(elements: BindingOrAssignmentElement[]) { + return createObjectLiteral(map(elements, convertToObjectAssignmentElement)); } - function createEffectiveBindingElement(name: Identifier) { + function makeBindingElement(name: Identifier) { return createBindingElement(/*propertyName*/ undefined, /*dotDotDotToken*/ undefined, name); } - function createEffectiveAssignmentElement(name: Identifier) { + function makeAssignmentElement(name: Identifier) { return name; } - function convertToArrayLiteralElement(element: EffectiveBindingOrAssignmentElement) { - if (isBindingElement(element)) { - if (element.dotDotDotToken) { - Debug.assertNode(element.name, isIdentifier); - return setOriginalNode(createSpread(element.name, element), element); - } - const expression = convertToExpressionTarget(element.name); - return element.initializer ? setOriginalNode(createAssignment(expression, element.initializer, element), element) : expression; - } - Debug.assertNode(element, isExpression); - return element; - } - - function convertToObjectLiteralElement(element: EffectiveBindingOrAssignmentElement) { - if (isBindingElement(element)) { - if (element.dotDotDotToken) { - Debug.assertNode(element.name, isIdentifier); - return setOriginalNode(createSpreadAssignment(element.name, element), element); - } - if (element.propertyName) { - const expression = convertToExpressionTarget(element.name); - return setOriginalNode(createPropertyAssignment(element.propertyName, element.initializer ? createAssignment(expression, element.initializer) : expression, element), element); - } - Debug.assertNode(element.name, isIdentifier); - return setOriginalNode(createShorthandPropertyAssignment(element.name, element.initializer, element), element); - } - Debug.assertNode(element, isObjectLiteralElementLike); - return element; - } - - function convertToExpressionTarget(target: EffectiveBindingOrAssignmentTarget): Expression { - if (isBindingPattern(target)) { - switch (target.kind) { - case SyntaxKind.ArrayBindingPattern: - return setOriginalNode(createArrayLiteral(map(target.elements, convertToArrayLiteralElement), target), target); - case SyntaxKind.ObjectBindingPattern: - return setOriginalNode(createObjectLiteral(map(target.elements, convertToObjectLiteralElement), target), target); - } - return; - } - Debug.assertNode(target, isExpression); - return target; - } - /** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement * `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);`*/ - function createRestCall(value: Expression, elements: EffectiveBindingOrAssignmentElement[], location: TextRange): Expression { + function createRestCall(value: Expression, elements: BindingOrAssignmentElement[], location: TextRange): Expression { const propertyNames: LiteralExpression[] = []; for (let i = 0; i < elements.length - 1; i++) { if (isOmittedExpression(elements[i])) { @@ -771,7 +434,7 @@ namespace ts { const str = createSynthesizedNode(SyntaxKind.StringLiteral); str.pos = location.pos; str.end = location.end; - str.text = getTextOfPropertyName(getEffectivePropertyNameOfEffectiveBindingElement(elements[i])); + str.text = getTextOfPropertyName(getPropertyNameOfBindingOrAssignmentElement(elements[i])); propertyNames.push(str); } const args = createSynthesizedNodeArray([value, createArrayLiteral(propertyNames, location)]); diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index dd7498c4149..29107e149f4 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -1365,7 +1365,7 @@ namespace ts { function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { // If we are here it is because this is a destructuring assignment. Debug.assert(isDestructuringAssignment(node)); - return flattenDestructuringToExpression( + return flattenDestructuringAssignment( context, node, needsDestructuringValue, @@ -1383,7 +1383,7 @@ namespace ts { if (decl.initializer) { let assignment: Expression; if (isBindingPattern(decl.name)) { - assignment = flattenDestructuringToExpression( + assignment = flattenDestructuringAssignment( context, decl, /*needsValue*/ false, @@ -1543,8 +1543,7 @@ namespace ts { if (isBindingPattern(node.name)) { const recordTempVariablesInLine = !enclosingVariableStatement || !hasModifier(enclosingVariableStatement, ModifierFlags.Export); - debugger; - return flattenDestructuringToDeclarations( + return flattenDestructuringBinding( context, node, /*value*/ undefined, @@ -2181,7 +2180,7 @@ namespace ts { const temp = createTempVariable(undefined); const newVariableDeclaration = createVariableDeclaration(temp, undefined, undefined, node.variableDeclaration); - const vars = flattenDestructuringToDeclarations(context, node.variableDeclaration, temp, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.All, visitor); + const vars = flattenDestructuringBinding(context, node.variableDeclaration, temp, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.All, visitor); const list = createVariableDeclarationList(vars, /*location*/node.variableDeclaration, /*flags*/node.variableDeclaration.flags); const destructure = createVariableStatement(undefined, list); diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index a52ee9b1a24..fb854318e69 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -135,7 +135,7 @@ namespace ts { */ function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsESNext) { - return flattenDestructuringToExpression( + return flattenDestructuringAssignment( context, node, needsDestructuringValue, @@ -156,7 +156,7 @@ namespace ts { function visitVariableDeclaration(node: VariableDeclaration): VisitResult { // If we are here it is because the name contains a binding pattern with a rest somewhere in it. if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.AssertESNext) { - return flattenDestructuringToDeclarations(context, node, /*boundValue*/ undefined, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.ObjectRest, visitor); + return flattenDestructuringBinding(context, node, /*boundValue*/ undefined, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.ObjectRest, visitor); } return visitEachChild(node, visitor, context); } @@ -202,14 +202,14 @@ namespace ts { const declaration = firstOrUndefined(initializer.declarations); return declaration && declaration.name && declaration.name.kind === SyntaxKind.ObjectBindingPattern && - !!(declaration.name.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)); + !!(declaration.name.transformFlags & TransformFlags.ContainsObjectRest); } return false; } function isRestAssignment(initializer: ForInitializer) { return initializer.kind === SyntaxKind.ObjectLiteralExpression && - initializer.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest); + initializer.transformFlags & TransformFlags.ContainsObjectRest; } function visitParameter(node: ParameterDeclaration): ParameterDeclaration { @@ -238,7 +238,7 @@ namespace ts { function isObjectRestParameter(node: ParameterDeclaration) { return node.name && node.name.kind === SyntaxKind.ObjectBindingPattern && - !!(node.name.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)); + !!(node.name.transformFlags & TransformFlags.ContainsObjectRest); } function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index bb17b99e5c6..8c65630aa5c 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -756,7 +756,7 @@ namespace ts { */ function transformInitializedVariable(node: VariableDeclaration): Expression { if (isBindingPattern(node.name)) { - return flattenDestructuringToExpression( + return flattenDestructuringAssignment( context, node, /*needsValue*/ false, diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 1ed1f82d506..3d89305f617 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -808,7 +808,7 @@ namespace ts { function transformInitializedVariable(node: VariableDeclaration, isExportedDeclaration: boolean): Expression { const createAssignment = isExportedDeclaration ? createExportedVariableAssignment : createNonExportedVariableAssignment; return isBindingPattern(node.name) - ? flattenDestructuringToExpression(context, node, /*needsValue*/ false, FlattenLevel.All, createAssignment, destructuringVisitor) + ? flattenDestructuringAssignment(context, node, /*needsValue*/ false, FlattenLevel.All, createAssignment, destructuringVisitor) : createAssignment(node.name, visitNode(node.initializer, destructuringVisitor, isExpression)); } @@ -1459,7 +1459,7 @@ namespace ts { */ function visitDestructuringAssignment(node: DestructuringAssignment): VisitResult { if (hasExportedReferenceInDestructuringTarget(node.left)) { - return flattenDestructuringToExpression( + return flattenDestructuringAssignment( context, node, /*needsValue*/ true, diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 118b170b6a4..4fc68566795 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2353,7 +2353,7 @@ namespace ts { function transformInitializedVariable(node: VariableDeclaration): Expression { const name = node.name; if (isBindingPattern(name)) { - return flattenDestructuringToExpression( + return flattenDestructuringAssignment( context, node, /*needsValue*/ false, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e0ba546fb96..0cda729f998 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -722,22 +722,20 @@ namespace ts { name: PropertyName; } - export interface BindingPattern extends Node { - elements: NodeArray; - } - - export interface ObjectBindingPattern extends BindingPattern { + export interface ObjectBindingPattern extends Node { kind: SyntaxKind.ObjectBindingPattern; elements: NodeArray; } - export type ArrayBindingElement = BindingElement | OmittedExpression; - - export interface ArrayBindingPattern extends BindingPattern { + export interface ArrayBindingPattern extends Node { kind: SyntaxKind.ArrayBindingPattern; elements: NodeArray; } + export type BindingPattern = (ObjectBindingPattern | ArrayBindingPattern) & { elements: NodeArray; }; + + export type ArrayBindingElement = BindingElement | OmittedExpression; + /** * Several node kinds share function-like features such as a signature, * a name, and a body. These nodes should extend FunctionLikeDeclaration. @@ -1191,7 +1189,49 @@ namespace ts { left: ArrayLiteralExpression; } - export type DestructuringAssignment = ObjectDestructuringAssignment | ArrayDestructuringAssignment; + export type DestructuringAssignment + = ObjectDestructuringAssignment + | ArrayDestructuringAssignment + ; + + export type BindingOrAssignmentElement + = VariableDeclaration + | ParameterDeclaration + | BindingElement + | PropertyAssignment // AssignmentProperty + | ShorthandPropertyAssignment // AssignmentProperty + | SpreadAssignment // AssignmentRestProperty + | OmittedExpression // Elision + | SpreadElement // AssignmentRestElement + | ArrayLiteralExpression // ArrayAssignmentPattern + | ObjectLiteralExpression // ObjectAssignmentPattern + | AssignmentExpression // AssignmentElement + | Identifier // DestructuringAssignmentTarget + | PropertyAccessExpression // DestructuringAssignmentTarget + | ElementAccessExpression // DestructuringAssignmentTarget + ; + + export type BindingOrAssignmentElementRestIndicator + = DotDotDotToken // from BindingElement + | SpreadElement // AssignmentRestElement + | SpreadAssignment // AssignmentRestProperty + ; + + export type BindingOrAssignmentElementTarget = BindingOrAssignmentPattern | Expression; + + export type ObjectBindingOrAssignmentPattern + = ObjectBindingPattern + | ObjectLiteralExpression // ObjectAssignmentPattern + ; + + export type ArrayBindingOrAssignmentPattern + = ArrayBindingPattern + | ArrayLiteralExpression // ArrayAssignmentPattern + ; + + export type AssignmentPattern = ObjectLiteralExpression | ArrayLiteralExpression; + + export type BindingOrAssignmentPattern = ObjectBindingOrAssignmentPattern | ArrayBindingOrAssignmentPattern; export interface ConditionalExpression extends Expression { kind: SyntaxKind.ConditionalExpression; @@ -3554,10 +3594,10 @@ namespace ts { TypeExcludes = ~ContainsTypeScript, ObjectLiteralExcludes = NodeExcludes | ContainsDecorators | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName | ContainsObjectSpread, ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsSpread, - VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern | ContainsObjectSpread, + VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern | ContainsObjectRest, ParameterExcludes = NodeExcludes, - CatchClauseExcludes = NodeExcludes | ContainsObjectSpread, - BindingPatternExcludes = NodeExcludes | ContainsSpread, + CatchClauseExcludes = NodeExcludes | ContainsObjectRest, + BindingPatternExcludes = NodeExcludes | ContainsRest, // Masks // - Additional bitmasks diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f0a666e1531..669b0d542ac 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3948,6 +3948,78 @@ namespace ts { || kind === SyntaxKind.OmittedExpression; } + + /** + * Determines whether the BindingOrAssignmentElement is a BindingElement-like declaration + */ + export function isDeclarationBindingElement(bindingElement: BindingOrAssignmentElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement { + switch (bindingElement.kind) { + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.BindingElement: + return true; + } + + return false; + } + + /** + * Determines whether a node is a BindingOrAssignmentElement + */ + export function isBindingOrAssignmentElement(node: Node): node is BindingOrAssignmentElement { + switch (node.kind) { + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.BindingElement: + case SyntaxKind.PropertyAssignment: + case SyntaxKind.ShorthandPropertyAssignment: + case SyntaxKind.SpreadAssignment: + case SyntaxKind.OmittedExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.Identifier: + case SyntaxKind.SpreadElement: + return true; + } + return isAssignmentExpression(node, /*excludeCompoundAssignment*/ true); + } + + /** + * Determines whether a node is a BindingOrAssignmentPattern + */ + export function isBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is BindingOrAssignmentPattern { + return isObjectBindingOrAssignmentPattern(node) + || isArrayBindingOrAssignmentPattern(node); + } + + /** + * Determines whether a node is an ObjectBindingOrAssignmentPattern + */ + export function isObjectBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is ObjectBindingOrAssignmentPattern { + switch (node.kind) { + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ObjectLiteralExpression: + return true; + } + + return false; + } + + /** + * Determines whether a node is an ArrayBindingOrAssignmentPattern + */ + export function isArrayBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is ArrayBindingOrAssignmentPattern { + switch (node.kind) { + case SyntaxKind.ArrayBindingPattern: + case SyntaxKind.ArrayLiteralExpression: + return true; + } + + return false; + } + // Expression export function isArrayLiteralExpression(node: Node): node is ArrayLiteralExpression { From 60d5195112ee7ab80ba01a3fbd874b7566158c01 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 14 Nov 2016 16:56:59 -0800 Subject: [PATCH 07/26] Update baselines, fix assignment check for object rest. --- Jakefile.js | 2 +- src/compiler/checker.ts | 6 ++-- src/compiler/diagnosticMessages.json | 2 +- src/compiler/factory.ts | 28 ++++++------------- src/compiler/transformers/destructuring.ts | 4 +++ src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 23 --------------- src/harness/runner.ts | 4 +++ .../reference/objectRestAssignment.js | 6 ++-- .../reference/objectRestNegative.errors.txt | 4 +-- .../baselines/reference/objectRestNegative.js | 2 +- 11 files changed, 27 insertions(+), 56 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 2e5d660c4f1..132a99e5087 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -640,7 +640,7 @@ task("importDefinitelyTypedTests", [importDefinitelyTypedTestsJs], function () { // Local target to build the compiler and services var tscFile = path.join(builtLocalDirectory, compilerFilename); -compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false); +compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false, { noMapRoot: true }); var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js"); var servicesFileInBrowserTest = path.join(builtLocalDirectory, "typescriptServicesInBrowserTest.js"); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 396715bfa45..730a435e67f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3081,7 +3081,7 @@ namespace ts { } const literalMembers: PropertyName[] = []; for (const element of pattern.elements) { - if (element.kind !== SyntaxKind.OmittedExpression && !(element as BindingElement).dotDotDotToken) { + if (!(element as BindingElement).dotDotDotToken) { literalMembers.push(element.propertyName || element.name as Identifier); } } @@ -14199,9 +14199,7 @@ namespace ts { } } else if (property.kind === SyntaxKind.SpreadAssignment) { - if (property.expression.kind !== SyntaxKind.Identifier) { - error(property.expression, Diagnostics.An_object_rest_element_must_be_an_identifier); - } + checkReferenceExpression(property.expression, Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access); } else { error(property, Diagnostics.Property_assignment_expected); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index be33335021b..a163a3bb641 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1991,7 +1991,7 @@ "category": "Error", "code": 2700 }, - "An object rest element must be an identifier.": { + "The target of an object rest assignment must be a variable or a property access.": { "category": "Error", "code": 2701 }, diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index c107263b9d8..d690eb84c78 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3284,9 +3284,9 @@ namespace ts { return bindingElement.right; } - if (isSpreadExpression(bindingElement) && isBindingOrAssignmentElement(bindingElement.expression)) { + if (isSpreadExpression(bindingElement)) { // Recovery consistent with existing emit. - return getInitializerOfBindingOrAssignmentElement(bindingElement.expression); + return getInitializerOfBindingOrAssignmentElement(bindingElement.expression); } } @@ -3327,9 +3327,7 @@ namespace ts { // `b.c` in `({ a: b.c = 1 } = ...)` // `b[0]` in `({ a: b[0] } = ...)` // `b[0]` in `({ a: b[0] = 1 } = ...)` - return isBindingOrAssignmentElement(bindingElement.initializer) - ? getTargetOfBindingOrAssignmentElement(bindingElement.initializer) - : undefined; + return getTargetOfBindingOrAssignmentElement(bindingElement.initializer); case SyntaxKind.ShorthandPropertyAssignment: // `a` in `({ a } = ...)` @@ -3338,9 +3336,7 @@ namespace ts { case SyntaxKind.SpreadAssignment: // `a` in `({ ...a } = ...)` - return isBindingOrAssignmentElement(bindingElement.expression) - ? getTargetOfBindingOrAssignmentElement(bindingElement.expression) - : undefined; + return getTargetOfBindingOrAssignmentElement(bindingElement.expression); } // no target @@ -3353,16 +3349,12 @@ namespace ts { // `[a]` in `[[a] = 1] = ...` // `a.b` in `[a.b = 1] = ...` // `a[0]` in `[a[0] = 1] = ...` - return isBindingOrAssignmentElement(bindingElement.left) - ? getTargetOfBindingOrAssignmentElement(bindingElement.left) - : undefined; + return getTargetOfBindingOrAssignmentElement(bindingElement.left); } if (isSpreadExpression(bindingElement)) { // `a` in `[...a] = ...` - return isBindingOrAssignmentElement(bindingElement.expression) - ? getTargetOfBindingOrAssignmentElement(bindingElement.expression) - : undefined; + return getTargetOfBindingOrAssignmentElement(bindingElement.expression); } // `a` in `[a] = ...` @@ -3442,18 +3434,14 @@ namespace ts { case SyntaxKind.ArrayLiteralExpression: // `a` in `{a}` // `a` in `[a]` - return map(name.elements, convertToBindingOrAssignmentElement); + return name.elements; case SyntaxKind.ObjectLiteralExpression: // `a` in `{a}` - return filter(name.properties, isBindingOrAssignmentElement); + return name.properties; } } - function convertToBindingOrAssignmentElement(node: Node) { - return isBindingOrAssignmentElement(node) ? node : createOmittedExpression(node); - } - export function convertToArrayAssignmentElement(element: BindingOrAssignmentElement) { if (isBindingElement(element)) { if (element.dotDotDotToken) { diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index b8da29ae291..433305ca6b2 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -292,6 +292,10 @@ namespace ts { // can perform the ObjectRest destructuring in a different declaration if (element.transformFlags & TransformFlags.ContainsObjectRest) { const temp = createTempVariable(/*recordTempVariable*/ undefined); + if (!host.recordTempVariablesInLine) { + host.context.hoistVariableDeclaration(temp); + } + restContainingElements = append(restContainingElements, <[Identifier, BindingOrAssignmentElement]>[temp, element]); bindingElements = append(bindingElements, host.createArrayBindingOrAssignmentElement(temp)); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 52b8eeb0fd3..8a6e74030d5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -734,7 +734,7 @@ namespace ts { elements: NodeArray; } - export type BindingPattern = (ObjectBindingPattern | ArrayBindingPattern) & { elements: NodeArray; }; + export type BindingPattern = ObjectBindingPattern | ArrayBindingPattern; export type ArrayBindingElement = BindingElement | OmittedExpression; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 52efb88f8c5..317cad49def 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3975,29 +3975,6 @@ namespace ts { return false; } - /** - * Determines whether a node is a BindingOrAssignmentElement - */ - export function isBindingOrAssignmentElement(node: Node): node is BindingOrAssignmentElement { - switch (node.kind) { - case SyntaxKind.VariableDeclaration: - case SyntaxKind.Parameter: - case SyntaxKind.BindingElement: - case SyntaxKind.PropertyAssignment: - case SyntaxKind.ShorthandPropertyAssignment: - case SyntaxKind.SpreadAssignment: - case SyntaxKind.OmittedExpression: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - case SyntaxKind.Identifier: - case SyntaxKind.SpreadElement: - return true; - } - return isAssignmentExpression(node, /*excludeCompoundAssignment*/ true); - } - /** * Determines whether a node is a BindingOrAssignmentPattern */ diff --git a/src/harness/runner.ts b/src/harness/runner.ts index 3db1da627bc..3ad6269e52f 100644 --- a/src/harness/runner.ts +++ b/src/harness/runner.ts @@ -61,6 +61,10 @@ function createRunner(kind: TestRunnerKind): RunnerBase { } } +if (Harness.IO.tryEnableSourceMapsForHost && /^development$/i.test(Harness.IO.getEnvironmentVariable("NODE_ENV"))) { + Harness.IO.tryEnableSourceMapsForHost(); +} + // users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options const mytestconfigFileName = "mytest.config"; diff --git a/tests/baselines/reference/objectRestAssignment.js b/tests/baselines/reference/objectRestAssignment.js index d4d266d3fe5..50d38c178a9 100644 --- a/tests/baselines/reference/objectRestAssignment.js +++ b/tests/baselines/reference/objectRestAssignment.js @@ -30,6 +30,6 @@ let complex; // should be: let overEmit; // var _g = overEmit.a, [_h, ...y] = _g, nested2 = __rest(_h, []), _j = overEmit.b, { z } = _j, c = __rest(_j, ["z"]), rest2 = __rest(overEmit, ["a", "b"]); -var _b = overEmit.a, [_c, ...y] = _b, nested2 = __rest(_c, []), _d = overEmit.b, { z } = _d, c = __rest(_d, ["z"]), rest2 = __rest(overEmit, ["a", "b"]); -(_e = overEmit.a, [_f, ...y] = _e, nested2 = __rest(_f, []), _g = overEmit.b, { z } = _g, c = __rest(_g, ["z"]), rest2 = __rest(overEmit, ["a", "b"])); -var _a, _e, _g; +var [_b, ...y] = overEmit.a, nested2 = __rest(_b, []), _c = overEmit.b, { z } = _c, c = __rest(_c, ["z"]), rest2 = __rest(overEmit, ["a", "b"]); +([_d, ...y] = overEmit.a, nested2 = __rest(_d, []), _e = overEmit.b, { z } = _e, c = __rest(_e, ["z"]), rest2 = __rest(overEmit, ["a", "b"])); +var _a, _d, _e; diff --git a/tests/baselines/reference/objectRestNegative.errors.txt b/tests/baselines/reference/objectRestNegative.errors.txt index b8144e3209b..793a7d31fc8 100644 --- a/tests/baselines/reference/objectRestNegative.errors.txt +++ b/tests/baselines/reference/objectRestNegative.errors.txt @@ -1,7 +1,7 @@ tests/cases/conformance/types/rest/objectRestNegative.ts(2,10): error TS2462: A rest element must be last in a destructuring pattern tests/cases/conformance/types/rest/objectRestNegative.ts(3,31): error TS2462: A rest element must be last in a destructuring pattern tests/cases/conformance/types/rest/objectRestNegative.ts(6,17): error TS2700: Rest types may only be created from object types. -tests/cases/conformance/types/rest/objectRestNegative.ts(11,9): error TS2701: An object rest element must be an identifier. +tests/cases/conformance/types/rest/objectRestNegative.ts(11,9): error TS2701: The target of an object rest assignment must be a variable or a property access. ==== tests/cases/conformance/types/rest/objectRestNegative.ts (4 errors) ==== @@ -23,5 +23,5 @@ tests/cases/conformance/types/rest/objectRestNegative.ts(11,9): error TS2701: An let rest: { b: string } ({a, ...rest.b + rest.b} = o); ~~~~~~~~~~~~~~~ -!!! error TS2701: An object rest element must be an identifier. +!!! error TS2701: The target of an object rest assignment must be a variable or a property access. \ No newline at end of file diff --git a/tests/baselines/reference/objectRestNegative.js b/tests/baselines/reference/objectRestNegative.js index 76e3cd765db..c9f0dba32c1 100644 --- a/tests/baselines/reference/objectRestNegative.js +++ b/tests/baselines/reference/objectRestNegative.js @@ -29,4 +29,4 @@ function generic(t) { return rest; } var rest; -(a = o.a, o, o); +(a = o.a, o, rest.b + rest.b = __rest(o, ["a"])); From cbdde421e63ce508b1a258ac8fe35d350f5218c8 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 14 Nov 2016 17:06:10 -0800 Subject: [PATCH 08/26] Moved the parenthesizing functions to make the diff easier to read --- src/compiler/factory.ts | 804 ++++++++++++++++++++-------------------- 1 file changed, 401 insertions(+), 403 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index d690eb84c78..919864628da 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2424,6 +2424,407 @@ namespace ts { return node; } + /** + * Wraps the operand to a BinaryExpression in parentheses if they are needed to preserve the intended + * order of operations. + * + * @param binaryOperator The operator for the BinaryExpression. + * @param operand The operand for the BinaryExpression. + * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the + * BinaryExpression. + */ + export function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand?: Expression) { + const skipped = skipPartiallyEmittedExpressions(operand); + + // If the resulting expression is already parenthesized, we do not need to do any further processing. + if (skipped.kind === SyntaxKind.ParenthesizedExpression) { + return operand; + } + + return binaryOperandNeedsParentheses(binaryOperator, operand, isLeftSideOfBinary, leftOperand) + ? createParen(operand) + : operand; + } + + /** + * Determines whether the operand to a BinaryExpression needs to be parenthesized. + * + * @param binaryOperator The operator for the BinaryExpression. + * @param operand The operand for the BinaryExpression. + * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the + * BinaryExpression. + */ + function binaryOperandNeedsParentheses(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand: Expression) { + // If the operand has lower precedence, then it needs to be parenthesized to preserve the + // intent of the expression. For example, if the operand is `a + b` and the operator is + // `*`, then we need to parenthesize the operand to preserve the intended order of + // operations: `(a + b) * x`. + // + // If the operand has higher precedence, then it does not need to be parenthesized. For + // example, if the operand is `a * b` and the operator is `+`, then we do not need to + // parenthesize to preserve the intended order of operations: `a * b + x`. + // + // If the operand has the same precedence, then we need to check the associativity of + // the operator based on whether this is the left or right operand of the expression. + // + // For example, if `a / d` is on the right of operator `*`, we need to parenthesize + // to preserve the intended order of operations: `x * (a / d)` + // + // If `a ** d` is on the left of operator `**`, we need to parenthesize to preserve + // the intended order of operations: `(a ** b) ** c` + const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator); + const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator); + const emittedOperand = skipPartiallyEmittedExpressions(operand); + const operandPrecedence = getExpressionPrecedence(emittedOperand); + switch (compareValues(operandPrecedence, binaryOperatorPrecedence)) { + case Comparison.LessThan: + // If the operand is the right side of a right-associative binary operation + // and is a yield expression, then we do not need parentheses. + if (!isLeftSideOfBinary + && binaryOperatorAssociativity === Associativity.Right + && operand.kind === SyntaxKind.YieldExpression) { + return false; + } + + return true; + + case Comparison.GreaterThan: + return false; + + case Comparison.EqualTo: + if (isLeftSideOfBinary) { + // No need to parenthesize the left operand when the binary operator is + // left associative: + // (a*b)/x -> a*b/x + // (a**b)/x -> a**b/x + // + // Parentheses are needed for the left operand when the binary operator is + // right associative: + // (a/b)**x -> (a/b)**x + // (a**b)**x -> (a**b)**x + return binaryOperatorAssociativity === Associativity.Right; + } + else { + if (isBinaryExpression(emittedOperand) + && emittedOperand.operatorToken.kind === binaryOperator) { + // No need to parenthesize the right operand when the binary operator and + // operand are the same and one of the following: + // x*(a*b) => x*a*b + // x|(a|b) => x|a|b + // x&(a&b) => x&a&b + // x^(a^b) => x^a^b + if (operatorHasAssociativeProperty(binaryOperator)) { + return false; + } + + // No need to parenthesize the right operand when the binary operator + // is plus (+) if both the left and right operands consist solely of either + // literals of the same kind or binary plus (+) expressions for literals of + // the same kind (recursively). + // "a"+(1+2) => "a"+(1+2) + // "a"+("b"+"c") => "a"+"b"+"c" + if (binaryOperator === SyntaxKind.PlusToken) { + const leftKind = leftOperand ? getLiteralKindOfBinaryPlusOperand(leftOperand) : SyntaxKind.Unknown; + if (isLiteralKind(leftKind) && leftKind === getLiteralKindOfBinaryPlusOperand(emittedOperand)) { + return false; + } + } + } + + // No need to parenthesize the right operand when the operand is right + // associative: + // x/(a**b) -> x/a**b + // x**(a**b) -> x**a**b + // + // Parentheses are needed for the right operand when the operand is left + // associative: + // x/(a*b) -> x/(a*b) + // x**(a/b) -> x**(a/b) + const operandAssociativity = getExpressionAssociativity(emittedOperand); + return operandAssociativity === Associativity.Left; + } + } + } + + /** + * Determines whether a binary operator is mathematically associative. + * + * @param binaryOperator The binary operator. + */ + function operatorHasAssociativeProperty(binaryOperator: SyntaxKind) { + // The following operators are associative in JavaScript: + // (a*b)*c -> a*(b*c) -> a*b*c + // (a|b)|c -> a|(b|c) -> a|b|c + // (a&b)&c -> a&(b&c) -> a&b&c + // (a^b)^c -> a^(b^c) -> a^b^c + // + // While addition is associative in mathematics, JavaScript's `+` is not + // guaranteed to be associative as it is overloaded with string concatenation. + return binaryOperator === SyntaxKind.AsteriskToken + || binaryOperator === SyntaxKind.BarToken + || binaryOperator === SyntaxKind.AmpersandToken + || binaryOperator === SyntaxKind.CaretToken; + } + + interface BinaryPlusExpression extends BinaryExpression { + cachedLiteralKind: SyntaxKind; + } + + /** + * This function determines whether an expression consists of a homogeneous set of + * literal expressions or binary plus expressions that all share the same literal kind. + * It is used to determine whether the right-hand operand of a binary plus expression can be + * emitted without parentheses. + */ + function getLiteralKindOfBinaryPlusOperand(node: Expression): SyntaxKind { + node = skipPartiallyEmittedExpressions(node); + + if (isLiteralKind(node.kind)) { + return node.kind; + } + + if (node.kind === SyntaxKind.BinaryExpression && (node).operatorToken.kind === SyntaxKind.PlusToken) { + if ((node).cachedLiteralKind !== undefined) { + return (node).cachedLiteralKind; + } + + const leftKind = getLiteralKindOfBinaryPlusOperand((node).left); + const literalKind = isLiteralKind(leftKind) + && leftKind === getLiteralKindOfBinaryPlusOperand((node).right) + ? leftKind + : SyntaxKind.Unknown; + + (node).cachedLiteralKind = literalKind; + return literalKind; + } + + return SyntaxKind.Unknown; + } + + /** + * 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 emittedExpression = skipPartiallyEmittedExpressions(expression); + switch (emittedExpression.kind) { + case SyntaxKind.CallExpression: + return createParen(expression); + + case SyntaxKind.NewExpression: + return (emittedExpression).arguments + ? expression + : createParen(expression); + } + + return parenthesizeForAccess(expression); + } + + /** + * Wraps an expression in parentheses if it is needed in order to use the expression for + * property or element access. + * + * @param expr The expression node. + */ + 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: + // + // NewExpression: + // new C.x -> not the same as (new C).x + // NumericLiteral + // 1.x -> not the same as (1).x + // + const emittedExpression = skipPartiallyEmittedExpressions(expression); + if (isLeftHandSideExpression(emittedExpression) + && (emittedExpression.kind !== SyntaxKind.NewExpression || (emittedExpression).arguments) + && emittedExpression.kind !== SyntaxKind.NumericLiteral) { + return expression; + } + + return createParen(expression, /*location*/ expression); + } + + 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); + } + + function parenthesizeListElements(elements: NodeArray) { + let result: Expression[]; + for (let i = 0; i < elements.length; i++) { + const element = parenthesizeExpressionForList(elements[i]); + if (result !== undefined || element !== elements[i]) { + if (result === undefined) { + result = elements.slice(0, i); + } + + result.push(element); + } + } + + if (result !== undefined) { + return createNodeArray(result, elements, elements.hasTrailingComma); + } + + return elements; + } + + export function parenthesizeExpressionForList(expression: Expression) { + const emittedExpression = skipPartiallyEmittedExpressions(expression); + const expressionPrecedence = getExpressionPrecedence(emittedExpression); + const commaPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, SyntaxKind.CommaToken); + return expressionPrecedence > commaPrecedence + ? expression + : createParen(expression, /*location*/ expression); + } + + export function parenthesizeExpressionForExpressionStatement(expression: Expression) { + const emittedExpression = skipPartiallyEmittedExpressions(expression); + if (isCallExpression(emittedExpression)) { + const callee = emittedExpression.expression; + const kind = skipPartiallyEmittedExpressions(callee).kind; + if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) { + const mutableCall = getMutableClone(emittedExpression); + mutableCall.expression = createParen(callee, /*location*/ callee); + return recreatePartiallyEmittedExpressions(expression, mutableCall); + } + } + else { + const leftmostExpressionKind = getLeftmostExpression(emittedExpression).kind; + if (leftmostExpressionKind === SyntaxKind.ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind.FunctionExpression) { + return createParen(expression, /*location*/ expression); + } + } + + return expression; + } + + /** + * Clones a series of not-emitted expressions with a new inner expression. + * + * @param originalOuterExpression The original outer expression. + * @param newInnerExpression The new inner expression. + */ + function recreatePartiallyEmittedExpressions(originalOuterExpression: Expression, newInnerExpression: Expression) { + if (isPartiallyEmittedExpression(originalOuterExpression)) { + const clone = getMutableClone(originalOuterExpression); + clone.expression = recreatePartiallyEmittedExpressions(clone.expression, newInnerExpression); + return clone; + } + + return newInnerExpression; + } + + 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; + + case SyntaxKind.PartiallyEmittedExpression: + node = (node).expression; + continue; + } + + return node; + } + } + + export function parenthesizeConciseBody(body: ConciseBody): ConciseBody { + const emittedBody = skipPartiallyEmittedExpressions(body); + if (emittedBody.kind === SyntaxKind.ObjectLiteralExpression) { + return createParen(body, /*location*/ body); + } + + return body; + } + + export const enum OuterExpressionKinds { + Parentheses = 1 << 0, + Assertions = 1 << 1, + PartiallyEmittedExpressions = 1 << 2, + + All = Parentheses | Assertions | PartiallyEmittedExpressions + } + + export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; + export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; + export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { + let previousNode: Node; + do { + previousNode = node; + if (kinds & OuterExpressionKinds.Parentheses) { + node = skipParentheses(node); + } + + if (kinds & OuterExpressionKinds.Assertions) { + node = skipAssertions(node); + } + + if (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) { + node = skipPartiallyEmittedExpressions(node); + } + } + while (previousNode !== node); + + return node; + } + + export function skipParentheses(node: Expression): Expression; + export function skipParentheses(node: Node): Node; + export function skipParentheses(node: Node): Node { + while (node.kind === SyntaxKind.ParenthesizedExpression) { + node = (node).expression; + } + + return node; + } + + export function skipAssertions(node: Expression): Expression; + export function skipAssertions(node: Node): Node; + export function skipAssertions(node: Node): Node { + while (isAssertionExpression(node)) { + node = (node).expression; + } + + return node; + } + + export function skipPartiallyEmittedExpressions(node: Expression): Expression; + export function skipPartiallyEmittedExpressions(node: Node): Node; + export function skipPartiallyEmittedExpressions(node: Node) { + while (node.kind === SyntaxKind.PartiallyEmittedExpression) { + node = (node).expression; + } + + return node; + } + export function startOnNewLine(node: T): T { node.startsOnNewLine = true; return node; @@ -3508,407 +3909,4 @@ namespace ts { Debug.assertNode(node, isExpression); return node; } - - // Parenthesizing - - /** - * Wraps the operand to a BinaryExpression in parentheses if they are needed to preserve the intended - * order of operations. - * - * @param binaryOperator The operator for the BinaryExpression. - * @param operand The operand for the BinaryExpression. - * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the - * BinaryExpression. - */ - export function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand?: Expression) { - const skipped = skipPartiallyEmittedExpressions(operand); - - // If the resulting expression is already parenthesized, we do not need to do any further processing. - if (skipped.kind === SyntaxKind.ParenthesizedExpression) { - return operand; - } - - return binaryOperandNeedsParentheses(binaryOperator, operand, isLeftSideOfBinary, leftOperand) - ? createParen(operand) - : operand; - } - - /** - * Determines whether the operand to a BinaryExpression needs to be parenthesized. - * - * @param binaryOperator The operator for the BinaryExpression. - * @param operand The operand for the BinaryExpression. - * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the - * BinaryExpression. - */ - function binaryOperandNeedsParentheses(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand: Expression) { - // If the operand has lower precedence, then it needs to be parenthesized to preserve the - // intent of the expression. For example, if the operand is `a + b` and the operator is - // `*`, then we need to parenthesize the operand to preserve the intended order of - // operations: `(a + b) * x`. - // - // If the operand has higher precedence, then it does not need to be parenthesized. For - // example, if the operand is `a * b` and the operator is `+`, then we do not need to - // parenthesize to preserve the intended order of operations: `a * b + x`. - // - // If the operand has the same precedence, then we need to check the associativity of - // the operator based on whether this is the left or right operand of the expression. - // - // For example, if `a / d` is on the right of operator `*`, we need to parenthesize - // to preserve the intended order of operations: `x * (a / d)` - // - // If `a ** d` is on the left of operator `**`, we need to parenthesize to preserve - // the intended order of operations: `(a ** b) ** c` - const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator); - const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator); - const emittedOperand = skipPartiallyEmittedExpressions(operand); - const operandPrecedence = getExpressionPrecedence(emittedOperand); - switch (compareValues(operandPrecedence, binaryOperatorPrecedence)) { - case Comparison.LessThan: - // If the operand is the right side of a right-associative binary operation - // and is a yield expression, then we do not need parentheses. - if (!isLeftSideOfBinary - && binaryOperatorAssociativity === Associativity.Right - && operand.kind === SyntaxKind.YieldExpression) { - return false; - } - - return true; - - case Comparison.GreaterThan: - return false; - - case Comparison.EqualTo: - if (isLeftSideOfBinary) { - // No need to parenthesize the left operand when the binary operator is - // left associative: - // (a*b)/x -> a*b/x - // (a**b)/x -> a**b/x - // - // Parentheses are needed for the left operand when the binary operator is - // right associative: - // (a/b)**x -> (a/b)**x - // (a**b)**x -> (a**b)**x - return binaryOperatorAssociativity === Associativity.Right; - } - else { - if (isBinaryExpression(emittedOperand) - && emittedOperand.operatorToken.kind === binaryOperator) { - // No need to parenthesize the right operand when the binary operator and - // operand are the same and one of the following: - // x*(a*b) => x*a*b - // x|(a|b) => x|a|b - // x&(a&b) => x&a&b - // x^(a^b) => x^a^b - if (operatorHasAssociativeProperty(binaryOperator)) { - return false; - } - - // No need to parenthesize the right operand when the binary operator - // is plus (+) if both the left and right operands consist solely of either - // literals of the same kind or binary plus (+) expressions for literals of - // the same kind (recursively). - // "a"+(1+2) => "a"+(1+2) - // "a"+("b"+"c") => "a"+"b"+"c" - if (binaryOperator === SyntaxKind.PlusToken) { - const leftKind = leftOperand ? getLiteralKindOfBinaryPlusOperand(leftOperand) : SyntaxKind.Unknown; - if (isLiteralKind(leftKind) && leftKind === getLiteralKindOfBinaryPlusOperand(emittedOperand)) { - return false; - } - } - } - - // No need to parenthesize the right operand when the operand is right - // associative: - // x/(a**b) -> x/a**b - // x**(a**b) -> x**a**b - // - // Parentheses are needed for the right operand when the operand is left - // associative: - // x/(a*b) -> x/(a*b) - // x**(a/b) -> x**(a/b) - const operandAssociativity = getExpressionAssociativity(emittedOperand); - return operandAssociativity === Associativity.Left; - } - } - } - - /** - * Determines whether a binary operator is mathematically associative. - * - * @param binaryOperator The binary operator. - */ - function operatorHasAssociativeProperty(binaryOperator: SyntaxKind) { - // The following operators are associative in JavaScript: - // (a*b)*c -> a*(b*c) -> a*b*c - // (a|b)|c -> a|(b|c) -> a|b|c - // (a&b)&c -> a&(b&c) -> a&b&c - // (a^b)^c -> a^(b^c) -> a^b^c - // - // While addition is associative in mathematics, JavaScript's `+` is not - // guaranteed to be associative as it is overloaded with string concatenation. - return binaryOperator === SyntaxKind.AsteriskToken - || binaryOperator === SyntaxKind.BarToken - || binaryOperator === SyntaxKind.AmpersandToken - || binaryOperator === SyntaxKind.CaretToken; - } - - interface BinaryPlusExpression extends BinaryExpression { - cachedLiteralKind: SyntaxKind; - } - - /** - * This function determines whether an expression consists of a homogeneous set of - * literal expressions or binary plus expressions that all share the same literal kind. - * It is used to determine whether the right-hand operand of a binary plus expression can be - * emitted without parentheses. - */ - function getLiteralKindOfBinaryPlusOperand(node: Expression): SyntaxKind { - node = skipPartiallyEmittedExpressions(node); - - if (isLiteralKind(node.kind)) { - return node.kind; - } - - if (node.kind === SyntaxKind.BinaryExpression && (node).operatorToken.kind === SyntaxKind.PlusToken) { - if ((node).cachedLiteralKind !== undefined) { - return (node).cachedLiteralKind; - } - - const leftKind = getLiteralKindOfBinaryPlusOperand((node).left); - const literalKind = isLiteralKind(leftKind) - && leftKind === getLiteralKindOfBinaryPlusOperand((node).right) - ? leftKind - : SyntaxKind.Unknown; - - (node).cachedLiteralKind = literalKind; - return literalKind; - } - - return SyntaxKind.Unknown; - } - - /** - * 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 emittedExpression = skipPartiallyEmittedExpressions(expression); - switch (emittedExpression.kind) { - case SyntaxKind.CallExpression: - return createParen(expression); - - case SyntaxKind.NewExpression: - return (emittedExpression).arguments - ? expression - : createParen(expression); - } - - return parenthesizeForAccess(expression); - } - - /** - * Wraps an expression in parentheses if it is needed in order to use the expression for - * property or element access. - * - * @param expr The expression node. - */ - 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: - // - // NewExpression: - // new C.x -> not the same as (new C).x - // NumericLiteral - // 1.x -> not the same as (1).x - // - const emittedExpression = skipPartiallyEmittedExpressions(expression); - if (isLeftHandSideExpression(emittedExpression) - && (emittedExpression.kind !== SyntaxKind.NewExpression || (emittedExpression).arguments) - && emittedExpression.kind !== SyntaxKind.NumericLiteral) { - return expression; - } - - return createParen(expression, /*location*/ expression); - } - - 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); - } - - function parenthesizeListElements(elements: NodeArray) { - let result: Expression[]; - for (let i = 0; i < elements.length; i++) { - const element = parenthesizeExpressionForList(elements[i]); - if (result !== undefined || element !== elements[i]) { - if (result === undefined) { - result = elements.slice(0, i); - } - - result.push(element); - } - } - - if (result !== undefined) { - return createNodeArray(result, elements, elements.hasTrailingComma); - } - - return elements; - } - - export function parenthesizeExpressionForList(expression: Expression) { - const emittedExpression = skipPartiallyEmittedExpressions(expression); - const expressionPrecedence = getExpressionPrecedence(emittedExpression); - const commaPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, SyntaxKind.CommaToken); - return expressionPrecedence > commaPrecedence - ? expression - : createParen(expression, /*location*/ expression); - } - - export function parenthesizeExpressionForExpressionStatement(expression: Expression) { - const emittedExpression = skipPartiallyEmittedExpressions(expression); - if (isCallExpression(emittedExpression)) { - const callee = emittedExpression.expression; - const kind = skipPartiallyEmittedExpressions(callee).kind; - if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) { - const mutableCall = getMutableClone(emittedExpression); - mutableCall.expression = createParen(callee, /*location*/ callee); - return recreatePartiallyEmittedExpressions(expression, mutableCall); - } - } - else { - const leftmostExpressionKind = getLeftmostExpression(emittedExpression).kind; - if (leftmostExpressionKind === SyntaxKind.ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind.FunctionExpression) { - return createParen(expression, /*location*/ expression); - } - } - - return expression; - } - - /** - * Clones a series of not-emitted expressions with a new inner expression. - * - * @param originalOuterExpression The original outer expression. - * @param newInnerExpression The new inner expression. - */ - function recreatePartiallyEmittedExpressions(originalOuterExpression: Expression, newInnerExpression: Expression) { - if (isPartiallyEmittedExpression(originalOuterExpression)) { - const clone = getMutableClone(originalOuterExpression); - clone.expression = recreatePartiallyEmittedExpressions(clone.expression, newInnerExpression); - return clone; - } - - return newInnerExpression; - } - - 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; - - case SyntaxKind.PartiallyEmittedExpression: - node = (node).expression; - continue; - } - - return node; - } - } - - export function parenthesizeConciseBody(body: ConciseBody): ConciseBody { - const emittedBody = skipPartiallyEmittedExpressions(body); - if (emittedBody.kind === SyntaxKind.ObjectLiteralExpression) { - return createParen(body, /*location*/ body); - } - - return body; - } - - export const enum OuterExpressionKinds { - Parentheses = 1 << 0, - Assertions = 1 << 1, - PartiallyEmittedExpressions = 1 << 2, - - All = Parentheses | Assertions | PartiallyEmittedExpressions - } - - export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; - export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; - export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { - let previousNode: Node; - do { - previousNode = node; - if (kinds & OuterExpressionKinds.Parentheses) { - node = skipParentheses(node); - } - - if (kinds & OuterExpressionKinds.Assertions) { - node = skipAssertions(node); - } - - if (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) { - node = skipPartiallyEmittedExpressions(node); - } - } - while (previousNode !== node); - - return node; - } - - export function skipParentheses(node: Expression): Expression; - export function skipParentheses(node: Node): Node; - export function skipParentheses(node: Node): Node { - while (node.kind === SyntaxKind.ParenthesizedExpression) { - node = (node).expression; - } - - return node; - } - - export function skipAssertions(node: Expression): Expression; - export function skipAssertions(node: Node): Node; - export function skipAssertions(node: Node): Node { - while (isAssertionExpression(node)) { - node = (node).expression; - } - - return node; - } - - export function skipPartiallyEmittedExpressions(node: Expression): Expression; - export function skipPartiallyEmittedExpressions(node: Node): Node; - export function skipPartiallyEmittedExpressions(node: Node) { - while (node.kind === SyntaxKind.PartiallyEmittedExpression) { - node = (node).expression; - } - - return node; - } } From ddcd22e4de4b604ee4ef532f48e3371fb4473352 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 14 Nov 2016 18:30:42 -0800 Subject: [PATCH 09/26] Removed esnext dependency on convertForOf/transformFunctionBody in factory --- src/compiler/factory.ts | 4 +- src/compiler/transformers/destructuring.ts | 10 +- src/compiler/transformers/esnext.ts | 305 +++++++++++++-------- src/compiler/utilities.ts | 6 + 4 files changed, 204 insertions(+), 121 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 919864628da..5727081dc78 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1528,6 +1528,8 @@ namespace ts { return createBinary(left, SyntaxKind.LessThanToken, right, location); } + export function createAssignment(left: ObjectLiteralExpression | ArrayLiteralExpression, right: Expression, location?: TextRange): DestructuringAssignment; + export function createAssignment(left: Expression, right: Expression, location?: TextRange): BinaryExpression; export function createAssignment(left: Expression, right: Expression, location?: TextRange) { return createBinary(left, SyntaxKind.EqualsToken, right, location); } @@ -2345,7 +2347,7 @@ namespace ts { if (isBlock(node)) { return node; } - return createBlock([createReturn(node, node)], node); + return createBlock([createReturn(node, node)], node, /*multiLine*/ true); } function isUseStrictPrologue(node: ExpressionStatement): boolean { diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 433305ca6b2..30484ffaf5c 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -54,7 +54,7 @@ namespace ts { } } - const expressions: Expression[] = []; + let expressions: Expression[]; const host: FlattenHost = { context, level, @@ -92,17 +92,21 @@ namespace ts { flattenBindingOrAssignmentElement(host, node, value, location, /*skipInitializer*/ isDestructuringAssignment(node)); if (value && needsValue) { + if (!some(expressions)) { + return value; + } + expressions.push(value); } - return aggregateTransformFlags(inlineExpressions(expressions)); + return aggregateTransformFlags(inlineExpressions(expressions)) || createOmittedExpression(); function emitExpression(expression: Expression) { // NOTE: this completely disables source maps, but aligns with the behavior of // `emitAssignment` in the old emitter. setEmitFlags(expression, EmitFlags.NoNestedSourceMaps); aggregateTransformFlags(expression); - expressions.push(expression); + expressions = append(expressions, expression); } function emitBindingOrAssignment(target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node) { diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index fb854318e69..85dd29d0725 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -4,6 +4,9 @@ /*@internal*/ namespace ts { export function transformESNext(context: TransformationContext) { + const { + endLexicalEnvironment + } = context; let currentSourceFile: SourceFile; return transformSourceFile; @@ -37,6 +40,14 @@ namespace ts { case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: return node; + case SyntaxKind.Constructor: + return visitConstructorDeclaration(node as ConstructorDeclaration); + case SyntaxKind.MethodDeclaration: + return visitMethodDeclaration(node as MethodDeclaration); + case SyntaxKind.GetAccessor: + return visitGetAccessorDeclaration(node as GetAccessorDeclaration); + case SyntaxKind.SetAccessor: + return visitSetAccessorDeclaration(node as SetAccessorDeclaration); case SyntaxKind.FunctionDeclaration: return visitFunctionDeclaration(node as FunctionDeclaration); case SyntaxKind.FunctionExpression: @@ -134,7 +145,7 @@ namespace ts { * @param node A BinaryExpression node. */ function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { - if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsESNext) { + if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRest) { return flattenDestructuringAssignment( context, node, @@ -144,7 +155,6 @@ namespace ts { visitor ); } - return visitEachChild(node, visitor, context); } @@ -155,8 +165,15 @@ namespace ts { */ function visitVariableDeclaration(node: VariableDeclaration): VisitResult { // If we are here it is because the name contains a binding pattern with a rest somewhere in it. - if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.AssertESNext) { - return flattenDestructuringBinding(context, node, /*boundValue*/ undefined, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.ObjectRest, visitor); + if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.ContainsObjectRest) { + return flattenDestructuringBinding( + context, + node, + /*boundValue*/ undefined, + /*skipInitializer*/ false, + /*recordTempVariablesInLine*/ true, + FlattenLevel.ObjectRest, + visitor); } return visitEachChild(node, visitor, context); } @@ -167,140 +184,194 @@ namespace ts { * @param node A ForOfStatement. */ function visitForOfStatement(node: ForOfStatement): VisitResult { - // The following ESNext code: - // - // for (let { x, y, ...rest } of expr) { } - // - // should be emitted as - // - // for (var _a of expr) { - // let { x, y } = _a, rest = __rest(_a, ["x", "y"]); - // } - // - // where _a is a temp emitted to capture the RHS. - // When the left hand side is an expression instead of a let declaration, - // the `let` before the `{ x, y }` 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. - - // for ( of ) - // where is [let] variabledeclarationlist | expression - const initializer = node.initializer; - if (!isRestBindingPattern(initializer) && !isRestAssignment(initializer)) { - return visitEachChild(node, visitor, context); + let leadingStatements: Statement[]; + let temp: Identifier; + const initializer = skipParentheses(node.initializer); + if (initializer.transformFlags & TransformFlags.ContainsObjectRest) { + if (isVariableDeclarationList(initializer)) { + temp = createTempVariable(/*recordTempVariable*/ undefined); + const firstDeclaration = firstOrUndefined(initializer.declarations); + const declarations = flattenDestructuringBinding( + context, + firstDeclaration, + temp, + /*skipInitializer*/ true, + /*recordTempVariablesInLine*/ true, + FlattenLevel.ObjectRest, + visitor + ); + if (some(declarations)) { + const statement = createVariableStatement( + /*modifiers*/ undefined, + updateVariableDeclarationList(initializer, declarations), + /*location*/ initializer + ); + leadingStatements = append(leadingStatements, statement); + } + } + else if (isAssignmentPattern(initializer)) { + temp = createTempVariable(/*recordTempVariable*/ undefined); + const expression = flattenDestructuringAssignment( + context, + aggregateTransformFlags(createAssignment(initializer, temp, /*location*/ node.initializer)), + /*needsValue*/ false, + FlattenLevel.ObjectRest, + /*createAssignmentCallback*/ undefined, + visitor + ); + leadingStatements = append(leadingStatements, createStatement(expression, /*location*/ node.initializer)); + } } - - return convertForOf(node, undefined, visitor, noop, context, /*transformRest*/ true); - } - - function isRestBindingPattern(initializer: ForInitializer) { - if (isVariableDeclarationList(initializer)) { - const declaration = firstOrUndefined(initializer.declarations); - return declaration && declaration.name && - declaration.name.kind === SyntaxKind.ObjectBindingPattern && - !!(declaration.name.transformFlags & TransformFlags.ContainsObjectRest); + if (temp) { + const expression = visitNode(node.expression, visitor, isExpression); + const statement = visitNode(node.statement, visitor, isStatement); + const block = isBlock(statement) + ? updateBlock(statement, createNodeArray(concatenate(leadingStatements, statement.statements), statement.statements)) + : createBlock(append(leadingStatements, statement), statement, /*multiLine*/ true); + return updateForOf( + node, + createVariableDeclarationList( + [ + createVariableDeclaration(temp, /*type*/ undefined, /*initializer*/ undefined, node.initializer) + ], + node.initializer, + NodeFlags.Let + ), + expression, + block + ); } - return false; - } - - function isRestAssignment(initializer: ForInitializer) { - return initializer.kind === SyntaxKind.ObjectLiteralExpression && - initializer.transformFlags & TransformFlags.ContainsObjectRest; + return visitEachChild(node, visitor, context); } function visitParameter(node: ParameterDeclaration): ParameterDeclaration { - if (isObjectRestParameter(node)) { + if (node.transformFlags & TransformFlags.ContainsObjectRest) { // Binding patterns are converted into a generated name and are // evaluated inside the function body. - return setOriginalNode( - createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotDotDotToken*/ undefined, - getGeneratedNameForNode(node), - /*questionToken*/ undefined, - /*type*/ undefined, - node.initializer, - /*location*/ node - ), - /*original*/ node + return updateParameter( + node, + /*decorators*/ undefined, + /*modifiers*/ undefined, + node.dotDotDotToken, + getGeneratedNameForNode(node), + /*type*/ undefined, + visitNode(node.initializer, visitor, isExpression) ); } - else { - return node; - } + return visitEachChild(node, visitor, context); } - function isObjectRestParameter(node: ParameterDeclaration) { - return node.name && - node.name.kind === SyntaxKind.ObjectBindingPattern && - !!(node.name.transformFlags & TransformFlags.ContainsObjectRest); + function visitConstructorDeclaration(node: ConstructorDeclaration) { + return updateConstructor( + node, + /*decorators*/ undefined, + node.modifiers, + visitParameterList(node.parameters, visitor, context), + transformFunctionBody(node) + ); } - function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { - const hasRest = forEach(node.parameters, isObjectRestParameter); - const body = hasRest ? - transformFunctionBody(node, visitor, currentSourceFile, context, noop, /*convertObjectRest*/ true) as Block : - visitEachChild(node.body, visitor, context); + function visitGetAccessorDeclaration(node: GetAccessorDeclaration) { + return updateGetAccessor( + node, + /*decorators*/ undefined, + node.modifiers, + visitNode(node.name, visitor, isPropertyName), + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + transformFunctionBody(node) + ); + } - return setOriginalNode( - createFunctionDeclaration( - /*decorators*/ undefined, - node.modifiers, - node.asteriskToken, - node.name, - /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), - /*type*/ undefined, - body, - /*location*/ node - ), - /*original*/ node); + function visitSetAccessorDeclaration(node: SetAccessorDeclaration) { + return updateSetAccessor( + node, + /*decorators*/ undefined, + node.modifiers, + visitNode(node.name, visitor, isPropertyName), + visitParameterList(node.parameters, visitor, context), + transformFunctionBody(node) + ); + } + + function visitMethodDeclaration(node: MethodDeclaration) { + return updateMethod( + node, + /*decorators*/ undefined, + node.modifiers, + visitNode(node.name, visitor, isPropertyName), + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + transformFunctionBody(node) + ); + } + + function visitFunctionDeclaration(node: FunctionDeclaration) { + return updateFunctionDeclaration( + node, + /*decorators*/ undefined, + node.modifiers, + node.name, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + transformFunctionBody(node) + ); } function visitArrowFunction(node: ArrowFunction) { - const hasRest = forEach(node.parameters, isObjectRestParameter); - const body = hasRest ? - transformFunctionBody(node, visitor, currentSourceFile, context, noop, /*convertObjectRest*/ true) as Block : - visitEachChild(node.body, visitor, context); - const func = setOriginalNode( - createArrowFunction( - /*modifiers*/ undefined, - /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), - /*type*/ undefined, - node.equalsGreaterThanToken, - body, - /*location*/ node - ), - /*original*/ node + return updateArrowFunction( + node, + node.modifiers, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + transformFunctionBody(node) ); - setEmitFlags(func, EmitFlags.CapturesThis); - return func; } - function visitFunctionExpression(node: FunctionExpression): Expression { - const hasRest = forEach(node.parameters, isObjectRestParameter); - const body = hasRest ? - transformFunctionBody(node, visitor, currentSourceFile, context, noop, /*convertObjectRest*/ true) as Block : - visitEachChild(node.body, visitor, context); - return setOriginalNode( - createFunctionExpression( - /*modifiers*/ undefined, - node.asteriskToken, - name, - /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), - /*type*/ undefined, - body, - /*location*/ node - ), - /*original*/ node + function visitFunctionExpression(node: FunctionExpression) { + return updateFunctionExpression( + node, + node.modifiers, + node.name, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + transformFunctionBody(node) ); } + + function transformFunctionBody(node: FunctionDeclaration | FunctionExpression | ConstructorDeclaration | MethodDeclaration | AccessorDeclaration): FunctionBody; + function transformFunctionBody(node: ArrowFunction): ConciseBody; + function transformFunctionBody(node: FunctionLikeDeclaration): ConciseBody { + let leadingStatements: Statement[]; + for (const parameter of node.parameters) { + if (parameter.transformFlags & TransformFlags.ContainsObjectRest) { + const temp = getGeneratedNameForNode(parameter); + const declarations = flattenDestructuringBinding(context, parameter, temp, /*skipInitializer*/ true, /*recordTempVariablesInLine*/ true, FlattenLevel.ObjectRest, visitor); + if (some(declarations)) { + const statement = createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList( + declarations + ) + ); + setEmitFlags(statement, EmitFlags.CustomPrologue); + leadingStatements = append(leadingStatements, statement); + } + } + } + + const body = visitNode(node.body, visitor, isConciseBody); + const trailingStatements = endLexicalEnvironment(); + if (some(leadingStatements) || some(trailingStatements)) { + const block = convertToFunctionBody(body); + return updateBlock(block, createNodeArray(concatenate(concatenate(leadingStatements, block.statements), trailingStatements), block.statements)); + } + + return body; + } } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 317cad49def..dd2afc38cbf 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3950,6 +3950,12 @@ namespace ts { return false; } + export function isAssignmentPattern(node: Node): node is AssignmentPattern { + const kind = node.kind; + return kind === SyntaxKind.ArrayLiteralExpression + || kind === SyntaxKind.ObjectLiteralExpression; + } + export function isBindingElement(node: Node): node is BindingElement { return node.kind === SyntaxKind.BindingElement; } From dff9849ac935c13f7cb184192ec40151a916c7f0 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 14 Nov 2016 18:33:21 -0800 Subject: [PATCH 10/26] Make multiLine optional in convertToFunctionBody --- src/compiler/factory.ts | 4 ++-- src/compiler/transformers/esnext.ts | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 5727081dc78..9c23cfd9b97 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2343,11 +2343,11 @@ namespace ts { return qualifiedName; } - export function convertToFunctionBody(node: ConciseBody) { + export function convertToFunctionBody(node: ConciseBody, multiLine?: boolean) { if (isBlock(node)) { return node; } - return createBlock([createReturn(node, node)], node, /*multiLine*/ true); + return createBlock([createReturn(node, node)], node, multiLine); } function isUseStrictPrologue(node: ExpressionStatement): boolean { diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 85dd29d0725..2a7fb855a92 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -363,14 +363,12 @@ namespace ts { } } } - const body = visitNode(node.body, visitor, isConciseBody); const trailingStatements = endLexicalEnvironment(); if (some(leadingStatements) || some(trailingStatements)) { - const block = convertToFunctionBody(body); + const block = convertToFunctionBody(body, /*multiLine*/ true); return updateBlock(block, createNodeArray(concatenate(concatenate(leadingStatements, block.statements), trailingStatements), block.statements)); } - return body; } } From b4d3bca32b59dbb4005f89154ba2a9654658a735 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 15 Nov 2016 09:50:29 -0800 Subject: [PATCH 11/26] Object rest emit for method and accessor parameters I missed these before, so emit was incorrect for object rest in a method or accessor parameter. --- src/compiler/binder.ts | 10 ++++ src/compiler/transformers/esnext.ts | 75 +++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 961ca205281..6b480699a55 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2771,6 +2771,11 @@ namespace ts { transformFlags |= TransformFlags.AssertTypeScript; } + // a method declaration with object rest destructuring is ES Next syntax + if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + transformFlags |= TransformFlags.AssertESNext; + } + // An async method declaration is ES2017 syntax. if (hasModifier(node, ModifierFlags.Async)) { transformFlags |= TransformFlags.AssertES2017; @@ -2797,6 +2802,11 @@ namespace ts { transformFlags |= TransformFlags.AssertTypeScript; } + // a method declaration with object rest destructuring is ES Next syntax + if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + transformFlags |= TransformFlags.AssertESNext; + } + node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; return transformFlags & ~TransformFlags.MethodOrAccessorExcludes; } diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 1fad209bdb3..0db09d79570 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -46,6 +46,11 @@ namespace ts { return visitFunctionExpression(node as FunctionExpression); case SyntaxKind.ArrowFunction: return visitArrowFunction(node as ArrowFunction); + case SyntaxKind.MethodDeclaration: + return visitMethodDeclaration(node as MethodDeclaration); + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return visitAccessorDeclaration(node as AccessorDeclaration); case SyntaxKind.Parameter: return visitParameter(node as ParameterDeclaration); default: @@ -208,11 +213,6 @@ namespace ts { } function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { - const hasRest = forEach(node.parameters, isObjectRestParameter); - const body = hasRest ? - transformFunctionBody(node, visitor, currentSourceFile, context, noop, /*convertObjectRest*/ true) as Block : - visitEachChild(node.body, visitor, context); - return setOriginalNode( createFunctionDeclaration( /*decorators*/ undefined, @@ -222,17 +222,13 @@ namespace ts { /*typeParameters*/ undefined, visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, - body, + transformFunctionBodyIfNeeded(node), /*location*/ node ), /*original*/ node); } function visitArrowFunction(node: ArrowFunction) { - const hasRest = forEach(node.parameters, isObjectRestParameter); - const body = hasRest ? - transformFunctionBody(node, visitor, currentSourceFile, context, noop, /*convertObjectRest*/ true) as Block : - visitEachChild(node.body, visitor, context); const func = setOriginalNode( createArrowFunction( /*modifiers*/ undefined, @@ -240,7 +236,7 @@ namespace ts { visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, node.equalsGreaterThanToken, - body, + transformFunctionBodyIfNeeded(node), /*location*/ node ), /*original*/ node @@ -250,10 +246,6 @@ namespace ts { } function visitFunctionExpression(node: FunctionExpression): Expression { - const hasRest = forEach(node.parameters, isObjectRestParameter); - const body = hasRest ? - transformFunctionBody(node, visitor, currentSourceFile, context, noop, /*convertObjectRest*/ true) as Block : - visitEachChild(node.body, visitor, context); return setOriginalNode( createFunctionExpression( /*modifiers*/ undefined, @@ -262,11 +254,62 @@ namespace ts { /*typeParameters*/ undefined, visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, - body, + transformFunctionBodyIfNeeded(node), /*location*/ node ), /*original*/ node ); } + + function visitMethodDeclaration(node: MethodDeclaration): MethodDeclaration { + return setOriginalNode( + createMethod( + /*decorators*/ undefined, + node.modifiers, + node.asteriskToken, + node.name, + /*typeParameters*/ undefined, + visitNodes(node.parameters, visitor, isParameter), + /*type*/ undefined, + transformFunctionBodyIfNeeded(node), + /*location*/ node + ), + /*original*/ node); + } + + function visitAccessorDeclaration(node: AccessorDeclaration): AccessorDeclaration { + if (node.kind === SyntaxKind.GetAccessor) { + return setOriginalNode( + createGetAccessor( + /*decorators*/ undefined, + node.modifiers, + node.name, + visitNodes(node.parameters, visitor, isParameter), + /*type*/ undefined, + transformFunctionBodyIfNeeded(node), + /*location*/ node + ), + /*original*/ node); + } + else { + return setOriginalNode( + createSetAccessor( + /*decorators*/ undefined, + node.modifiers, + node.name, + visitNodes(node.parameters, visitor, isParameter), + transformFunctionBodyIfNeeded(node), + /*location*/ node + ), + /*original*/ node); + } + } + + function transformFunctionBodyIfNeeded(node: FunctionLikeDeclaration): Block { + const hasRest = forEach(node.parameters, isObjectRestParameter); + return hasRest ? + transformFunctionBody(node, visitor, currentSourceFile, context, noop, /*convertObjectRest*/ true) : + visitEachChild(node.body, visitor, context) as Block; + } } } From 498d978c216701e25046da7b3e5eda13f55f4a6f Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 15 Nov 2016 09:55:56 -0800 Subject: [PATCH 12/26] Test object rest parameter emit: methods/accessors --- .../reference/objectRestParameter.js | 19 +++++++++++++++ .../reference/objectRestParameter.symbols | 23 +++++++++++++++++++ .../reference/objectRestParameter.types | 23 +++++++++++++++++++ .../types/rest/objectRestParameter.ts | 9 ++++++++ 4 files changed, 74 insertions(+) diff --git a/tests/baselines/reference/objectRestParameter.js b/tests/baselines/reference/objectRestParameter.js index 49434f24eec..a05448a8be2 100644 --- a/tests/baselines/reference/objectRestParameter.js +++ b/tests/baselines/reference/objectRestParameter.js @@ -6,6 +6,15 @@ declare function suddenly(f: (a: { x: { z, ka }, y: string }) => void); suddenly(({ x: a, ...rest }) => rest.y); suddenly(({ x: { z = 12, ...nested }, ...rest } = { x: { z: 1, ka: 1 }, y: 'noo' }) => rest.y + nested.ka); +class C { + m({ a, ...clone }: { a: number, b: string}): void { + // actually, never mind, don't clone + } + set p({ a, ...clone }: { a: number, b: string}) { + // actually, never mind, don't clone + } +} + //// [objectRestParameter.js] @@ -26,3 +35,13 @@ suddenly((_a = { x: { z: 1, ka: 1 }, y: 'noo' }) => { var _b = _a.x, { z = 12 } = _b, nested = __rest(_b, ["z"]), rest = __rest(_a, ["x"]); return rest.y + nested.ka; }); +class C { + m(_a) { + var { a } = _a, clone = __rest(_a, ["a"]); + // actually, never mind, don't clone + } + set p(_a) { + var { a } = _a, clone = __rest(_a, ["a"]); + // actually, never mind, don't clone + } +} diff --git a/tests/baselines/reference/objectRestParameter.symbols b/tests/baselines/reference/objectRestParameter.symbols index 9569dc53d3a..c43a8ba5a04 100644 --- a/tests/baselines/reference/objectRestParameter.symbols +++ b/tests/baselines/reference/objectRestParameter.symbols @@ -42,4 +42,27 @@ suddenly(({ x: { z = 12, ...nested }, ...rest } = { x: { z: 1, ka: 1 }, y: 'noo' >nested : Symbol(nested, Decl(objectRestParameter.ts, 5, 24)) >ka : Symbol(ka, Decl(objectRestParameter.ts, 3, 42)) +class C { +>C : Symbol(C, Decl(objectRestParameter.ts, 5, 107)) + + m({ a, ...clone }: { a: number, b: string}): void { +>m : Symbol(C.m, Decl(objectRestParameter.ts, 7, 9)) +>a : Symbol(a, Decl(objectRestParameter.ts, 8, 7)) +>clone : Symbol(clone, Decl(objectRestParameter.ts, 8, 10)) +>a : Symbol(a, Decl(objectRestParameter.ts, 8, 24)) +>b : Symbol(b, Decl(objectRestParameter.ts, 8, 35)) + + // actually, never mind, don't clone + } + set p({ a, ...clone }: { a: number, b: string}) { +>p : Symbol(C.p, Decl(objectRestParameter.ts, 10, 5)) +>a : Symbol(a, Decl(objectRestParameter.ts, 11, 11)) +>clone : Symbol(clone, Decl(objectRestParameter.ts, 11, 14)) +>a : Symbol(a, Decl(objectRestParameter.ts, 11, 28)) +>b : Symbol(b, Decl(objectRestParameter.ts, 11, 39)) + + // actually, never mind, don't clone + } +} + diff --git a/tests/baselines/reference/objectRestParameter.types b/tests/baselines/reference/objectRestParameter.types index c634c9c5cd5..56e7352870f 100644 --- a/tests/baselines/reference/objectRestParameter.types +++ b/tests/baselines/reference/objectRestParameter.types @@ -53,4 +53,27 @@ suddenly(({ x: { z = 12, ...nested }, ...rest } = { x: { z: 1, ka: 1 }, y: 'noo' >nested : { ka: any; } >ka : any +class C { +>C : C + + m({ a, ...clone }: { a: number, b: string}): void { +>m : ({a, ...clone}: { a: number; b: string; }) => void +>a : number +>clone : { b: string; } +>a : number +>b : string + + // actually, never mind, don't clone + } + set p({ a, ...clone }: { a: number, b: string}) { +>p : { a: number; b: string; } +>a : number +>clone : { b: string; } +>a : number +>b : string + + // actually, never mind, don't clone + } +} + diff --git a/tests/cases/conformance/types/rest/objectRestParameter.ts b/tests/cases/conformance/types/rest/objectRestParameter.ts index 5b47442f047..a9c17a29d14 100644 --- a/tests/cases/conformance/types/rest/objectRestParameter.ts +++ b/tests/cases/conformance/types/rest/objectRestParameter.ts @@ -6,3 +6,12 @@ declare function suddenly(f: (a: { x: { z, ka }, y: string }) => void); suddenly(({ x: a, ...rest }) => rest.y); suddenly(({ x: { z = 12, ...nested }, ...rest } = { x: { z: 1, ka: 1 }, y: 'noo' }) => rest.y + nested.ka); +class C { + m({ a, ...clone }: { a: number, b: string}): void { + // actually, never mind, don't clone + } + set p({ a, ...clone }: { a: number, b: string}) { + // actually, never mind, don't clone + } +} + From 98e192f3571c87936524c9c1865b33916dad6eb0 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 15 Nov 2016 12:32:43 -0800 Subject: [PATCH 13/26] Move transformFunctionBody back into es2015 --- src/compiler/factory.ts | 561 +------ src/compiler/transformers/es2015.ts | 518 +++++- src/compiler/transformers/es2016.ts | 113 +- src/compiler/transformers/esnext.ts | 110 +- src/compiler/types.ts | 56 +- tests/baselines/reference/objectRestForOf.js | 8 +- ...ngForObjectBindingPatternDefaultValues2.js | 27 +- ...rObjectBindingPatternDefaultValues2.js.map | 2 +- ...BindingPatternDefaultValues2.sourcemap.txt | 1485 ++++++++--------- 9 files changed, 1405 insertions(+), 1475 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 9c23cfd9b97..44db12f7b6c 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -648,7 +648,7 @@ namespace ts { export function createConditional(condition: Expression, questionToken: QuestionToken, whenTrue: Expression, colonToken: ColonToken, whenFalse: Expression, location?: TextRange) { const node = createNode(SyntaxKind.ConditionalExpression, location); - node.condition = condition; + node.condition = parenthesizeForConditionalHead(condition); node.questionToken = questionToken; node.whenTrue = whenTrue; node.colonToken = colonToken; @@ -2603,6 +2603,16 @@ namespace ts { return SyntaxKind.Unknown; } + export function parenthesizeForConditionalHead(condition: Expression) { + const conditionalPrecedence = getOperatorPrecedence(SyntaxKind.ConditionalExpression, SyntaxKind.QuestionToken); + const emittedCondition = skipPartiallyEmittedExpressions(condition); + const conditionPrecedence = getExpressionPrecedence(emittedCondition); + if (compareValues(conditionPrecedence, conditionalPrecedence) === Comparison.LessThan) { + return createParen(condition); + } + return condition; + } + /** * Wraps an expression in parentheses if it is needed in order to use the expression * as the expression of a NewExpression node. @@ -3102,555 +3112,6 @@ namespace ts { return tryGetModuleNameFromFile(resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions); } - /** - * Transforms the body of a function-like node. - * - * @param node A function-like node. - */ - export function transformFunctionBody(node: FunctionLikeDeclaration, - visitor: (node: Node) => VisitResult, - currentSourceFile: SourceFile, - context: TransformationContext, - enableSubstitutionsForCapturedThis: () => void, - convertObjectRest?: boolean) { - let multiLine = false; // indicates whether the block *must* be emitted as multiple lines - let singleLine = false; // indicates whether the block *may* be emitted as a single line - let statementsLocation: TextRange; - let closeBraceLocation: TextRange; - - const statements: Statement[] = []; - const body = node.body; - let statementOffset: number; - - context.startLexicalEnvironment(); - if (isBlock(body)) { - // ensureUseStrict is false because no new prologue-directive should be added. - // addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array - statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor); - } - - addCaptureThisForNodeIfNeeded(statements, node, enableSubstitutionsForCapturedThis); - addDefaultValueAssignmentsIfNeeded(context, statements, node, visitor, convertObjectRest); - addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false); - - // If we added any generated statements, this must be a multi-line block. - if (!multiLine && statements.length > 0) { - multiLine = true; - } - - if (isBlock(body)) { - statementsLocation = body.statements; - addRange(statements, visitNodes(body.statements, visitor, isStatement, statementOffset)); - - // If the original body was a multi-line block, this must be a multi-line block. - if (!multiLine && body.multiLine) { - multiLine = true; - } - } - else { - Debug.assert(node.kind === SyntaxKind.ArrowFunction); - - // To align with the old emitter, we use a synthetic end position on the location - // for the statement list we synthesize when we down-level an arrow function with - // an expression function body. This prevents both comments and source maps from - // being emitted for the end position only. - statementsLocation = moveRangeEnd(body, -1); - - const equalsGreaterThanToken = (node).equalsGreaterThanToken; - if (!nodeIsSynthesized(equalsGreaterThanToken) && !nodeIsSynthesized(body)) { - if (rangeEndIsOnSameLineAsRangeStart(equalsGreaterThanToken, body, currentSourceFile)) { - singleLine = true; - } - else { - multiLine = true; - } - } - - const expression = visitNode(body, visitor, isExpression); - const returnStatement = createReturn(expression, /*location*/ body); - setEmitFlags(returnStatement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTrailingComments); - statements.push(returnStatement); - - // To align with the source map emit for the old emitter, we set a custom - // source map location for the close brace. - closeBraceLocation = body; - } - - const lexicalEnvironment = context.endLexicalEnvironment(); - addRange(statements, lexicalEnvironment); - - // If we added any final generated statements, this must be a multi-line block - if (!multiLine && lexicalEnvironment && lexicalEnvironment.length) { - multiLine = true; - } - - const block = createBlock(createNodeArray(statements, statementsLocation), node.body, multiLine); - if (!multiLine && singleLine) { - setEmitFlags(block, EmitFlags.SingleLine); - } - - if (closeBraceLocation) { - setTokenSourceMapRange(block, SyntaxKind.CloseBraceToken, closeBraceLocation); - } - - setOriginalNode(block, node.body); - return block; - } - - /** - * 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. - */ - export function addCaptureThisForNodeIfNeeded(statements: Statement[], node: Node, enableSubstitutionsForCapturedThis: () => void): void { - if (node.transformFlags & TransformFlags.ContainsCapturedLexicalThis && node.kind !== SyntaxKind.ArrowFunction) { - captureThisForNode(statements, node, createThis(), enableSubstitutionsForCapturedThis); - } - } - - export function captureThisForNode(statements: Statement[], node: Node, initializer: Expression | undefined, enableSubstitutionsForCapturedThis?: () => void, originalStatement?: Statement): void { - enableSubstitutionsForCapturedThis(); - const captureThisStatement = createVariableStatement( - /*modifiers*/ undefined, - createVariableDeclarationList([ - createVariableDeclaration( - "_this", - /*type*/ undefined, - initializer - ) - ]), - originalStatement - ); - - setEmitFlags(captureThisStatement, EmitFlags.NoComments | EmitFlags.CustomPrologue); - setSourceMapRange(captureThisStatement, node); - statements.push(captureThisStatement); - } - - /** - * 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; - } - - /** - * 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. - */ - export function addDefaultValueAssignmentsIfNeeded(context: TransformationContext, - statements: Statement[], - node: FunctionLikeDeclaration, - visitor: (node: Node) => VisitResult, - convertObjectRest: boolean): 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(context, statements, parameter, name, initializer, visitor, convertObjectRest); - } - else if (initializer) { - addDefaultValueAssignmentForInitializer(statements, parameter, name, initializer, visitor); - } - } - } - - /** - * 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(context: TransformationContext, - statements: Statement[], - parameter: ParameterDeclaration, - name: BindingPattern, initializer: Expression, - visitor: (node: Node) => VisitResult, - convertObjectRest: boolean): 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) { - statements.push( - setEmitFlags( - createVariableStatement( - /*modifiers*/ undefined, - createVariableDeclarationList( - flattenDestructuringBinding( - context, - parameter, - temp, - /*skipInitializer*/ convertObjectRest, - /*recordTempVariablesInLine*/ true, - convertObjectRest - ? FlattenLevel.ObjectRest - : FlattenLevel.All, - visitor - ) - ) - ), - EmitFlags.CustomPrologue - ) - ); - } - else if (initializer) { - statements.push( - setEmitFlags( - createStatement( - createAssignment( - temp, - visitNode(initializer, visitor, isExpression) - ) - ), - EmitFlags.CustomPrologue - ) - ); - } - } - - /** - * 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, - visitor: (node: Node) => VisitResult): void { - initializer = visitNode(initializer, visitor, isExpression); - const statement = createIf( - createStrictEquality( - getSynthesizedClone(name), - createVoidZero() - ), - setEmitFlags( - createBlock([ - createStatement( - createAssignment( - setEmitFlags(getMutableClone(name), EmitFlags.NoSourceMap), - setEmitFlags(initializer, EmitFlags.NoSourceMap | getEmitFlags(initializer)), - /*location*/ parameter - ) - ) - ], /*location*/ parameter), - EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps - ), - /*elseStatement*/ undefined, - /*location*/ parameter - ); - statement.startsOnNewLine = true; - setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue); - statements.push(statement); - } - - /** - * 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 && node.name.kind === SyntaxKind.Identifier && !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` - */ - export function addRestParameterIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper: boolean): void { - const parameter = lastOrUndefined(node.parameters); - if (!shouldAddRestParameter(parameter, inConstructorWithSynthesizedSuper)) { - return; - } - - // `declarationName` is the name of the local declaration for the parameter. - const declarationName = getMutableClone(parameter.name); - setEmitFlags(declarationName, EmitFlags.NoSourceMap); - - // `expressionName` is the name of the parameter used in expressions. - const expressionName = getSynthesizedClone(parameter.name); - const restIndex = node.parameters.length - 1; - const temp = createLoopVariable(); - - // var param = []; - statements.push( - setEmitFlags( - createVariableStatement( - /*modifiers*/ undefined, - createVariableDeclarationList([ - createVariableDeclaration( - declarationName, - /*type*/ undefined, - createArrayLiteral([]) - ) - ]), - /*location*/ parameter - ), - EmitFlags.CustomPrologue - ) - ); - - // for (var _i = restIndex; _i < arguments.length; _i++) { - // param[_i - restIndex] = arguments[_i]; - // } - const forStatement = createFor( - createVariableDeclarationList([ - createVariableDeclaration(temp, /*type*/ undefined, createLiteral(restIndex)) - ], /*location*/ parameter), - createLessThan( - temp, - createPropertyAccess(createIdentifier("arguments"), "length"), - /*location*/ parameter - ), - createPostfixIncrement(temp, /*location*/ parameter), - createBlock([ - startOnNewLine( - createStatement( - createAssignment( - createElementAccess( - expressionName, - createSubtract(temp, createLiteral(restIndex)) - ), - createElementAccess(createIdentifier("arguments"), temp) - ), - /*location*/ parameter - ) - ) - ]) - ); - - setEmitFlags(forStatement, EmitFlags.CustomPrologue); - startOnNewLine(forStatement); - statements.push(forStatement); - } - - export function convertForOf(node: ForOfStatement, convertedLoopBodyStatements: Statement[], - visitor: (node: Node) => VisitResult, - enableSubstitutionsForBlockScopedBindings: () => void, - context: TransformationContext, - convertObjectRest?: boolean): ForStatement | ForOfStatement { - // 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 initializer = node.initializer; - const statements: 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 = convertObjectRest ? undefined : createLoopVariable(); - const rhsReference = expression.kind === SyntaxKind.Identifier - ? createUniqueName((expression).text) - : createTempVariable(/*recordTempVariable*/ undefined); - const elementAccess = convertObjectRest ? rhsReference : createElementAccess(rhsReference, counter); - - // Initialize LHS - // var v = _a[_i]; - if (isVariableDeclarationList(initializer)) { - if (initializer.flags & NodeFlags.BlockScoped) { - enableSubstitutionsForBlockScopedBindings(); - } - - const firstOriginalDeclaration = firstOrUndefined(initializer.declarations); - if (firstOriginalDeclaration && isBindingPattern(firstOriginalDeclaration.name)) { - // This works whether the declaration is a var, let, or const. - // It will use rhsIterationValue _a[_i] as the initializer. - const declarations = flattenDestructuringBinding( - context, - firstOriginalDeclaration, - elementAccess, - /*skipInitializer*/ false, - /*recordTempVariablesInLine*/ true, - convertObjectRest - ? FlattenLevel.ObjectRest - : FlattenLevel.All, - visitor - ); - - const declarationList = createVariableDeclarationList(declarations, /*location*/ initializer); - setOriginalNode(declarationList, initializer); - - // Adjust the source map range for the first declaration to align with the old - // emitter. - const firstDeclaration = declarations[0]; - const lastDeclaration = lastOrUndefined(declarations); - setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end)); - - statements.push( - createVariableStatement( - /*modifiers*/ undefined, - declarationList - ) - ); - } - else { - // The following call does not include the initializer, so we have - // to emit it separately. - statements.push( - createVariableStatement( - /*modifiers*/ undefined, - setOriginalNode( - createVariableDeclarationList([ - createVariableDeclaration( - firstOriginalDeclaration ? firstOriginalDeclaration.name : createTempVariable(/*recordTempVariable*/ undefined), - /*type*/ undefined, - createElementAccess(rhsReference, counter) - ) - ], /*location*/ moveRangePos(initializer, -1)), - initializer - ), - /*location*/ moveRangeEnd(initializer, -1) - ) - ); - } - } - else { - // Initializer is an expression. Emit the expression in the body, so that it's - // evaluated on every iteration. - const assignment = createAssignment(initializer, elementAccess); - if (isDestructuringAssignment(assignment)) { - // This is a destructuring pattern, so we flatten the destructuring instead. - statements.push( - createStatement( - flattenDestructuringAssignment( - context, - assignment, - /*needsValue*/ false, - convertObjectRest - ? FlattenLevel.ObjectRest - : FlattenLevel.All, - /*createAssignmentCallback*/ undefined, - visitor - ) - ) - ); - } - else { - // Currently there is not way to check that assignment is binary expression of destructing assignment - // so we have to cast never type to binaryExpression - (assignment).end = initializer.end; - statements.push(createStatement(assignment, /*location*/ moveRangeEnd(initializer, -1))); - } - } - - let bodyLocation: TextRange; - let statementsLocation: TextRange; - if (convertedLoopBodyStatements) { - addRange(statements, convertedLoopBodyStatements); - } - else { - const statement = visitNode(node.statement, visitor, isStatement); - if (isBlock(statement)) { - addRange(statements, statement.statements); - bodyLocation = statement; - statementsLocation = statement.statements; - } - else { - statements.push(statement); - } - } - - // The old emitter does not emit source maps for the expression - setEmitFlags(expression, EmitFlags.NoSourceMap | getEmitFlags(expression)); - - // The old emitter does not emit source maps for the block. - // We add the location to preserve comments. - const body = createBlock( - createNodeArray(statements, /*location*/ statementsLocation), - /*location*/ bodyLocation - ); - - setEmitFlags(body, EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps); - - let forStatement: ForStatement | ForOfStatement; - if (convertObjectRest) { - forStatement = createForOf( - createVariableDeclarationList([ - createVariableDeclaration(rhsReference, /*type*/ undefined, /*initializer*/ undefined, /*location*/ node.expression) - ], /*location*/ node.expression), - node.expression, - body, - /*location*/ node - ); - } - else { - forStatement = createFor( - setEmitFlags( - createVariableDeclarationList([ - createVariableDeclaration(counter, /*type*/ undefined, createLiteral(0), /*location*/ moveRangePos(node.expression, -1)), - createVariableDeclaration(rhsReference, /*type*/ undefined, expression, /*location*/ node.expression) - ], /*location*/ node.expression), - EmitFlags.NoHoisting - ), - createLessThan( - counter, - createPropertyAccess(rhsReference, "length"), - /*location*/ node.expression - ), - createPostfixIncrement(counter, /*location*/ node.expression), - body, - /*location*/ node - ); - } - - // Disable trailing source maps for the OpenParenToken to align source map emit with the old emitter. - setEmitFlags(forStatement, EmitFlags.NoTokenTrailingSourceMaps); - return forStatement; - } - /** * Gets the initializer of an BindingOrAssignmentElement. */ diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 29107e149f4..0085fcef1bc 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -861,7 +861,7 @@ namespace ts { } if (constructor) { - addDefaultValueAssignmentsIfNeeded(context, statements, constructor, visitor, /*convertObjectRest*/ false); + addDefaultValueAssignmentsIfNeeded(statements, constructor); addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper); Debug.assert(statementOffset >= 0, "statementOffset not initialized correctly!"); @@ -954,7 +954,7 @@ namespace ts { // If this isn't a derived class, just capture 'this' for arrow functions if necessary. if (!hasExtendsClause) { if (ctor) { - addCaptureThisForNodeIfNeeded(statements, ctor, enableSubstitutionsForCapturedThis); + addCaptureThisForNodeIfNeeded(statements, ctor); } return SuperCaptureResult.NoReplacement; } @@ -1016,7 +1016,7 @@ namespace ts { } // Perform the capture. - captureThisForNode(statements, ctor, superCallExpression, enableSubstitutionsForCapturedThis, firstStatement); + captureThisForNode(statements, ctor, superCallExpression, firstStatement); // If we're actually replacing the original statement, we need to signal this to the caller. if (superCallExpression) { @@ -1085,6 +1085,250 @@ 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; + } + + /** + * 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; + } + + 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); + } + } + } + + /** + * 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); + + // 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) { + statements.push( + setEmitFlags( + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList( + flattenDestructuringBinding( + context, + parameter, + temp, + /*skipInitializer*/ false, + /*recordTempVariablesInLine*/ true, + FlattenLevel.All, + visitor + ) + ) + ), + EmitFlags.CustomPrologue + ) + ); + } + else if (initializer) { + statements.push( + setEmitFlags( + createStatement( + createAssignment( + temp, + visitNode(initializer, visitor, isExpression) + ) + ), + EmitFlags.CustomPrologue + ) + ); + } + } + + /** + * 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 { + initializer = visitNode(initializer, visitor, isExpression); + const statement = createIf( + createStrictEquality( + getSynthesizedClone(name), + createVoidZero() + ), + setEmitFlags( + createBlock([ + createStatement( + createAssignment( + setEmitFlags(getMutableClone(name), EmitFlags.NoSourceMap), + setEmitFlags(initializer, EmitFlags.NoSourceMap | getEmitFlags(initializer)), + /*location*/ parameter + ) + ) + ], /*location*/ parameter), + EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps + ), + /*elseStatement*/ undefined, + /*location*/ parameter + ); + statement.startsOnNewLine = true; + setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue); + statements.push(statement); + } + + /** + * 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 && node.name.kind === SyntaxKind.Identifier && !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, inConstructorWithSynthesizedSuper)) { + return; + } + + // `declarationName` is the name of the local declaration for the parameter. + const declarationName = getMutableClone(parameter.name); + setEmitFlags(declarationName, EmitFlags.NoSourceMap); + + // `expressionName` is the name of the parameter used in expressions. + const expressionName = getSynthesizedClone(parameter.name); + const restIndex = node.parameters.length - 1; + const temp = createLoopVariable(); + + // var param = []; + statements.push( + setEmitFlags( + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + declarationName, + /*type*/ undefined, + createArrayLiteral([]) + ) + ]), + /*location*/ parameter + ), + EmitFlags.CustomPrologue + ) + ); + + // for (var _i = restIndex; _i < arguments.length; _i++) { + // param[_i - restIndex] = arguments[_i]; + // } + const forStatement = createFor( + createVariableDeclarationList([ + createVariableDeclaration(temp, /*type*/ undefined, createLiteral(restIndex)) + ], /*location*/ parameter), + createLessThan( + temp, + createPropertyAccess(createIdentifier("arguments"), "length"), + /*location*/ parameter + ), + createPostfixIncrement(temp, /*location*/ parameter), + createBlock([ + startOnNewLine( + createStatement( + createAssignment( + createElementAccess( + expressionName, + createSubtract(temp, createLiteral(restIndex)) + ), + createElementAccess(createIdentifier("arguments"), temp) + ), + /*location*/ parameter + ) + ) + ]) + ); + + setEmitFlags(forStatement, EmitFlags.CustomPrologue); + startOnNewLine(forStatement); + statements.push(forStatement); + } + + /** + * 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) { + captureThisForNode(statements, node, createThis()); + } + } + + function captureThisForNode(statements: Statement[], node: Node, initializer: Expression | undefined, originalStatement?: Statement): void { + enableSubstitutionsForCapturedThis(); + const captureThisStatement = createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + "_this", + /*type*/ undefined, + initializer + ) + ]), + originalStatement + ); + + setEmitFlags(captureThisStatement, EmitFlags.NoComments | EmitFlags.CustomPrologue); + setSourceMapRange(captureThisStatement, node); + statements.push(captureThisStatement); + } + /** * Adds statements to the class body function for a class to define the members of the * class. @@ -1282,7 +1526,7 @@ namespace ts { /*typeParameters*/ undefined, visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, - transformFunctionBody(node, visitor, currentSourceFile, context, enableSubstitutionsForCapturedThis), + transformFunctionBody(node), /*location*/ node ), /*original*/ node); @@ -1309,7 +1553,7 @@ namespace ts { /*typeParameters*/ undefined, visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, - saveStateAndInvoke(node, node => transformFunctionBody(node, visitor, currentSourceFile, context, enableSubstitutionsForCapturedThis)), + saveStateAndInvoke(node, transformFunctionBody), location ), /*original*/ node @@ -1319,6 +1563,96 @@ namespace ts { return expression; } + /** + * Transforms the body of a function-like node. + * + * @param node A function-like node. + */ + function transformFunctionBody(node: FunctionLikeDeclaration) { + let multiLine = false; // indicates whether the block *must* be emitted as multiple lines + let singleLine = false; // indicates whether the block *may* be emitted as a single line + let statementsLocation: TextRange; + let closeBraceLocation: TextRange; + + const statements: Statement[] = []; + const body = node.body; + let statementOffset: number; + + context.startLexicalEnvironment(); + if (isBlock(body)) { + // ensureUseStrict is false because no new prologue-directive should be added. + // addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array + statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor); + } + + addCaptureThisForNodeIfNeeded(statements, node); + addDefaultValueAssignmentsIfNeeded(statements, node); + addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false); + + // If we added any generated statements, this must be a multi-line block. + if (!multiLine && statements.length > 0) { + multiLine = true; + } + + if (isBlock(body)) { + statementsLocation = body.statements; + addRange(statements, visitNodes(body.statements, visitor, isStatement, statementOffset)); + + // If the original body was a multi-line block, this must be a multi-line block. + if (!multiLine && body.multiLine) { + multiLine = true; + } + } + else { + Debug.assert(node.kind === SyntaxKind.ArrowFunction); + + // To align with the old emitter, we use a synthetic end position on the location + // for the statement list we synthesize when we down-level an arrow function with + // an expression function body. This prevents both comments and source maps from + // being emitted for the end position only. + statementsLocation = moveRangeEnd(body, -1); + + const equalsGreaterThanToken = (node).equalsGreaterThanToken; + if (!nodeIsSynthesized(equalsGreaterThanToken) && !nodeIsSynthesized(body)) { + if (rangeEndIsOnSameLineAsRangeStart(equalsGreaterThanToken, body, currentSourceFile)) { + singleLine = true; + } + else { + multiLine = true; + } + } + + const expression = visitNode(body, visitor, isExpression); + const returnStatement = createReturn(expression, /*location*/ body); + setEmitFlags(returnStatement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTrailingComments); + statements.push(returnStatement); + + // To align with the source map emit for the old emitter, we set a custom + // source map location for the close brace. + closeBraceLocation = body; + } + + const lexicalEnvironment = context.endLexicalEnvironment(); + addRange(statements, lexicalEnvironment); + + // If we added any final generated statements, this must be a multi-line block + if (!multiLine && lexicalEnvironment && lexicalEnvironment.length) { + multiLine = true; + } + + const block = createBlock(createNodeArray(statements, statementsLocation), node.body, multiLine); + if (!multiLine && singleLine) { + setEmitFlags(block, EmitFlags.SingleLine); + } + + if (closeBraceLocation) { + setTokenSourceMapRange(block, SyntaxKind.CloseBraceToken, closeBraceLocation); + } + + setOriginalNode(block, node.body); + return block; + } + /** * Visits an ExpressionStatement that contains a destructuring assignment. * @@ -1606,7 +1940,177 @@ namespace ts { } function convertForOfToFor(node: ForOfStatement, convertedLoopBodyStatements: Statement[]): ForStatement { - return convertForOf(node, convertedLoopBodyStatements, visitor, enableSubstitutionsForBlockScopedBindings, context, /*transformRest*/ false); + // 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 initializer = node.initializer; + const statements: 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 + ? createUniqueName((expression).text) + : createTempVariable(/*recordTempVariable*/ undefined); + const elementAccess = createElementAccess(rhsReference, counter); + + // Initialize LHS + // var v = _a[_i]; + if (isVariableDeclarationList(initializer)) { + if (initializer.flags & NodeFlags.BlockScoped) { + enableSubstitutionsForBlockScopedBindings(); + } + + const firstOriginalDeclaration = firstOrUndefined(initializer.declarations); + if (firstOriginalDeclaration && isBindingPattern(firstOriginalDeclaration.name)) { + // This works whether the declaration is a var, let, or const. + // It will use rhsIterationValue _a[_i] as the initializer. + const declarations = flattenDestructuringBinding( + context, + firstOriginalDeclaration, + elementAccess, + /*skipInitializer*/ false, + /*recordTempVariablesInLine*/ true, + FlattenLevel.All, + visitor + ); + + const declarationList = createVariableDeclarationList(declarations, /*location*/ initializer); + setOriginalNode(declarationList, initializer); + + // Adjust the source map range for the first declaration to align with the old + // emitter. + const firstDeclaration = declarations[0]; + const lastDeclaration = lastOrUndefined(declarations); + setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end)); + + statements.push( + createVariableStatement( + /*modifiers*/ undefined, + declarationList + ) + ); + } + else { + // The following call does not include the initializer, so we have + // to emit it separately. + statements.push( + createVariableStatement( + /*modifiers*/ undefined, + setOriginalNode( + createVariableDeclarationList([ + createVariableDeclaration( + firstOriginalDeclaration ? firstOriginalDeclaration.name : createTempVariable(/*recordTempVariable*/ undefined), + /*type*/ undefined, + createElementAccess(rhsReference, counter) + ) + ], /*location*/ moveRangePos(initializer, -1)), + initializer + ), + /*location*/ moveRangeEnd(initializer, -1) + ) + ); + } + } + else { + // Initializer is an expression. Emit the expression in the body, so that it's + // evaluated on every iteration. + const assignment = createAssignment(initializer, elementAccess); + if (isDestructuringAssignment(assignment)) { + // This is a destructuring pattern, so we flatten the destructuring instead. + statements.push( + createStatement( + flattenDestructuringAssignment( + context, + assignment, + /*needsValue*/ false, + FlattenLevel.All, + /*createAssignmentCallback*/ undefined, + visitor + ) + ) + ); + } + else { + // Currently there is not way to check that assignment is binary expression of destructing assignment + // so we have to cast never type to binaryExpression + (assignment).end = initializer.end; + statements.push(createStatement(assignment, /*location*/ moveRangeEnd(initializer, -1))); + } + } + + let bodyLocation: TextRange; + let statementsLocation: TextRange; + if (convertedLoopBodyStatements) { + addRange(statements, convertedLoopBodyStatements); + } + else { + const statement = visitNode(node.statement, visitor, isStatement); + if (isBlock(statement)) { + addRange(statements, statement.statements); + bodyLocation = statement; + statementsLocation = statement.statements; + } + else { + statements.push(statement); + } + } + + // The old emitter does not emit source maps for the expression + setEmitFlags(expression, EmitFlags.NoSourceMap | getEmitFlags(expression)); + + // The old emitter does not emit source maps for the block. + // We add the location to preserve comments. + const body = createBlock( + createNodeArray(statements, /*location*/ statementsLocation), + /*location*/ bodyLocation + ); + + setEmitFlags(body, EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps); + + const forStatement = createFor( + setEmitFlags( + createVariableDeclarationList([ + createVariableDeclaration(counter, /*type*/ undefined, createLiteral(0), /*location*/ moveRangePos(node.expression, -1)), + createVariableDeclaration(rhsReference, /*type*/ undefined, expression, /*location*/ node.expression) + ], /*location*/ node.expression), + EmitFlags.NoHoisting + ), + createLessThan( + counter, + createPropertyAccess(rhsReference, "length"), + /*location*/ node.expression + ), + createPostfixIncrement(counter, /*location*/ node.expression), + body, + /*location*/ node + ); + + // Disable trailing source maps for the OpenParenToken to align source map emit with the old emitter. + setEmitFlags(forStatement, EmitFlags.NoTokenTrailingSourceMaps); + return forStatement; } /** @@ -2592,7 +3096,7 @@ namespace ts { const statements: Statement[] = []; startLexicalEnvironment(); addRange(statements, prologue); - addCaptureThisForNodeIfNeeded(statements, node, enableSubstitutionsForCapturedThis); + addCaptureThisForNodeIfNeeded(statements, node); addRange(statements, visitNodes(createNodeArray(remaining), visitor, isStatement)); addRange(statements, endLexicalEnvironment()); const clone = getMutableClone(node); diff --git a/src/compiler/transformers/es2016.ts b/src/compiler/transformers/es2016.ts index 56d7fb0249a..5959753fcef 100644 --- a/src/compiler/transformers/es2016.ts +++ b/src/compiler/transformers/es2016.ts @@ -17,84 +17,75 @@ namespace ts { } function visitor(node: Node): VisitResult { - if (node.transformFlags & TransformFlags.ES2016) { - return visitorWorker(node); - } - else if (node.transformFlags & TransformFlags.ContainsES2016) { - return visitEachChild(node, visitor, context); - } - else { + if ((node.transformFlags & TransformFlags.ContainsES2016) === 0) { return node; } - } - - function visitorWorker(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.BinaryExpression: return visitBinaryExpression(node); default: - Debug.failBadSyntaxKind(node); return visitEachChild(node, visitor, context); } } function visitBinaryExpression(node: BinaryExpression): Expression { - // We are here because ES2016 adds support for the exponentiation operator. + switch (node.operatorToken.kind) { + case SyntaxKind.AsteriskAsteriskEqualsToken: + return visitExponentiationAssignmentExpression(node); + case SyntaxKind.AsteriskAsteriskToken: + return visitExponentiationExpression(node); + default: + return visitEachChild(node, visitor, context); + } + } + + function visitExponentiationAssignmentExpression(node: BinaryExpression) { + let target: Expression; + let value: Expression; const left = visitNode(node.left, visitor, isExpression); const right = visitNode(node.right, visitor, isExpression); - if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { - let target: Expression; - let value: Expression; - if (isElementAccessExpression(left)) { - // Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)` - const expressionTemp = createTempVariable(hoistVariableDeclaration); - - const argumentExpressionTemp = createTempVariable(hoistVariableDeclaration); - - target = createElementAccess( - createAssignment(expressionTemp, left.expression, /*location*/ left.expression), - createAssignment(argumentExpressionTemp, left.argumentExpression, /*location*/ left.argumentExpression), - /*location*/ left - ); - - value = createElementAccess( - expressionTemp, - argumentExpressionTemp, - /*location*/ left - ); - } - else if (isPropertyAccessExpression(left)) { - // Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)` - const expressionTemp = createTempVariable(hoistVariableDeclaration); - - target = createPropertyAccess( - createAssignment(expressionTemp, left.expression, /*location*/ left.expression), - left.name, - /*location*/ left - ); - - value = createPropertyAccess( - expressionTemp, - left.name, - /*location*/ left - ); - } - else { - // Transforms `a **= b` into `a = Math.pow(a, b)` - target = left; - value = left; - } - - return createAssignment(target, createMathPow(value, right, /*location*/ node), /*location*/ node); + if (isElementAccessExpression(left)) { + // Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)` + const expressionTemp = createTempVariable(hoistVariableDeclaration); + const argumentExpressionTemp = createTempVariable(hoistVariableDeclaration); + target = createElementAccess( + createAssignment(expressionTemp, left.expression, /*location*/ left.expression), + createAssignment(argumentExpressionTemp, left.argumentExpression, /*location*/ left.argumentExpression), + /*location*/ left + ); + value = createElementAccess( + expressionTemp, + argumentExpressionTemp, + /*location*/ left + ); } - else if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken) { - // Transforms `a ** b` into `Math.pow(a, b)` - return createMathPow(left, right, /*location*/ node); + else if (isPropertyAccessExpression(left)) { + // Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)` + const expressionTemp = createTempVariable(hoistVariableDeclaration); + target = createPropertyAccess( + createAssignment(expressionTemp, left.expression, /*location*/ left.expression), + left.name, + /*location*/ left + ); + value = createPropertyAccess( + expressionTemp, + left.name, + /*location*/ left + ); } else { - Debug.failBadSyntaxKind(node); - return visitEachChild(node, visitor, context); + // Transforms `a **= b` into `a = Math.pow(a, b)` + target = left; + value = left; } + return createAssignment(target, createMathPow(value, right, /*location*/ node), /*location*/ node); + } + + function visitExponentiationExpression(node: BinaryExpression) { + // Transforms `a ** b` into `Math.pow(a, b)` + const left = visitNode(node.left, visitor, isExpression); + const right = visitNode(node.right, visitor, isExpression); + return createMathPow(left, right, /*location*/ node); } } } diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 2a7fb855a92..757fd7957c9 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -7,39 +7,38 @@ namespace ts { const { endLexicalEnvironment } = context; - let currentSourceFile: SourceFile; return transformSourceFile; function transformSourceFile(node: SourceFile) { - currentSourceFile = node; return visitEachChild(node, visitor, context); } function visitor(node: Node): VisitResult { - if (node.transformFlags & TransformFlags.ESNext) { - return visitorWorker(node); - } - else if (node.transformFlags & TransformFlags.ContainsESNext) { - return visitNodeContainingESNext(node); - } - else { - return node; - } + return visitorWorker(node, /*noDestructuringValue*/ false); } - function visitorWorker(node: Node): VisitResult { + function visitorNoDestructuringValue(node: Node): VisitResult { + return visitorWorker(node, /*noDestructuringValue*/ true); + } + + function visitorWorker(node: Node, noDestructuringValue: boolean): VisitResult { + if ((node.transformFlags & TransformFlags.ContainsESNext) === 0) { + return node; + } + switch (node.kind) { case SyntaxKind.ObjectLiteralExpression: return visitObjectLiteralExpression(node as ObjectLiteralExpression); case SyntaxKind.BinaryExpression: - return visitBinaryExpression(node as BinaryExpression, /*needsDestructuringValue*/ true); + return visitBinaryExpression(node as BinaryExpression, noDestructuringValue); case SyntaxKind.VariableDeclaration: return visitVariableDeclaration(node as VariableDeclaration); case SyntaxKind.ForOfStatement: return visitForOfStatement(node as ForOfStatement); - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ArrayBindingPattern: - return node; + case SyntaxKind.ForStatement: + return visitForStatement(node as ForStatement); + case SyntaxKind.VoidExpression: + return visitVoidExpression(node as VoidExpression); case SyntaxKind.Constructor: return visitConstructorDeclaration(node as ConstructorDeclaration); case SyntaxKind.MethodDeclaration: @@ -56,20 +55,13 @@ namespace ts { return visitArrowFunction(node as ArrowFunction); case SyntaxKind.Parameter: return visitParameter(node as ParameterDeclaration); - default: - Debug.failBadSyntaxKind(node); - return visitEachChild(node, visitor, context); - } - } - - function visitNodeContainingESNext(node: Node) { - switch (node.kind) { case SyntaxKind.ExpressionStatement: return visitExpressionStatement(node as ExpressionStatement); case SyntaxKind.ParenthesizedExpression: - return visitParenthesizedExpression(node as ParenthesizedExpression, /*needsDestructuringValue*/ true); + return visitParenthesizedExpression(node as ParenthesizedExpression, noDestructuringValue); + default: + return visitEachChild(node, visitor, context); } - return visitEachChild(node, visitor, context); } function chunkObjectLiteralElements(elements: ObjectLiteralElement[]): Expression[] { @@ -105,38 +97,27 @@ namespace ts { } function visitObjectLiteralExpression(node: ObjectLiteralExpression): Expression { - // spread elements emit like so: - // non-spread elements are chunked together into object literals, and then all are passed to __assign: - // { a, ...o, b } => __assign({a}, o, {b}); - // If the first element is a spread element, then the first argument to __assign is {}: - // { ...o, a, b, ...o2 } => __assign({}, o, {a, b}, o2) - const objects = chunkObjectLiteralElements(node.properties); - if (objects.length && objects[0].kind !== SyntaxKind.ObjectLiteralExpression) { - objects.unshift(createObjectLiteral()); + if (node.transformFlags & TransformFlags.ContainsObjectSpread) { + // spread elements emit like so: + // non-spread elements are chunked together into object literals, and then all are passed to __assign: + // { a, ...o, b } => __assign({a}, o, {b}); + // If the first element is a spread element, then the first argument to __assign is {}: + // { ...o, a, b, ...o2 } => __assign({}, o, {a, b}, o2) + const objects = chunkObjectLiteralElements(node.properties); + if (objects.length && objects[0].kind !== SyntaxKind.ObjectLiteralExpression) { + objects.unshift(createObjectLiteral()); + } + return createCall(createIdentifier("__assign"), undefined, objects); } - return createCall(createIdentifier("__assign"), undefined, objects); + return visitEachChild(node, visitor, context); } function visitExpressionStatement(node: ExpressionStatement): ExpressionStatement { - switch (node.expression.kind) { - case SyntaxKind.ParenthesizedExpression: - return updateStatement(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); - case SyntaxKind.BinaryExpression: - return updateStatement(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); - } - return visitEachChild(node, visitor, context); + return visitEachChild(node, visitorNoDestructuringValue, context); } - function visitParenthesizedExpression(node: ParenthesizedExpression, needsDestructuringValue: boolean): ParenthesizedExpression { - if (!needsDestructuringValue) { - switch (node.expression.kind) { - case SyntaxKind.ParenthesizedExpression: - return updateParen(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); - case SyntaxKind.BinaryExpression: - return updateParen(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); - } - } - return visitEachChild(node, visitor, context); + function visitParenthesizedExpression(node: ParenthesizedExpression, noDestructuringValue: boolean): ParenthesizedExpression { + return visitEachChild(node, noDestructuringValue ? visitorNoDestructuringValue : visitor, context); } /** @@ -144,17 +125,24 @@ namespace ts { * * @param node A BinaryExpression node. */ - function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { + function visitBinaryExpression(node: BinaryExpression, noDestructuringValue: boolean): Expression { if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRest) { return flattenDestructuringAssignment( context, node, - needsDestructuringValue, + !noDestructuringValue, FlattenLevel.ObjectRest, /*createAssignmentCallback*/ undefined, visitor ); } + else if (node.operatorToken.kind === SyntaxKind.CommaToken) { + return updateBinary( + node, + visitNode(node.left, visitorNoDestructuringValue, isExpression), + visitNode(node.right, noDestructuringValue ? visitorNoDestructuringValue : visitor, isExpression) + ); + } return visitEachChild(node, visitor, context); } @@ -178,6 +166,20 @@ namespace ts { return visitEachChild(node, visitor, context); } + function visitForStatement(node: ForStatement): VisitResult { + return updateFor( + node, + visitNode(node.initializer, visitorNoDestructuringValue, isForInitializer), + visitNode(node.condition, visitor, isExpression), + visitNode(node.incrementor, visitor, isExpression), + visitNode(node.statement, visitor, isStatement) + ); + } + + function visitVoidExpression(node: VoidExpression) { + return visitEachChild(node, visitorNoDestructuringValue, context); + } + /** * Visits a ForOfStatement and converts it into a ES2015-compatible ForOfStatement. * diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8a6e74030d5..b22fba32da9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2721,7 +2721,7 @@ namespace ts { resolvedSignature?: Signature; // Cached signature of signature node or call expression resolvedSymbol?: Symbol; // Cached name resolution result resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result - maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate + maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate enumMemberValue?: number; // Constant value of enum member isVisible?: boolean; // Is this node visible hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context @@ -3558,36 +3558,34 @@ namespace ts { TypeScript = 1 << 0, ContainsTypeScript = 1 << 1, ContainsJsx = 1 << 2, - ESNext = 1 << 3, - ContainsESNext = 1 << 4, - ContainsES2017 = 1 << 5, - ES2016 = 1 << 6, - ContainsES2016 = 1 << 7, - ES2015 = 1 << 8, - ContainsES2015 = 1 << 9, - Generator = 1 << 10, - ContainsGenerator = 1 << 11, - DestructuringAssignment = 1 << 12, - ContainsDestructuringAssignment = 1 << 13, + ContainsESNext = 1 << 3, + ContainsES2017 = 1 << 4, + ContainsES2016 = 1 << 5, + ES2015 = 1 << 6, + ContainsES2015 = 1 << 7, + Generator = 1 << 8, + ContainsGenerator = 1 << 9, + DestructuringAssignment = 1 << 10, + ContainsDestructuringAssignment = 1 << 11, // Markers // - Flags used to indicate that a subtree contains a specific transformation. - ContainsDecorators = 1 << 14, - ContainsPropertyInitializer = 1 << 15, - ContainsLexicalThis = 1 << 16, - ContainsCapturedLexicalThis = 1 << 17, - ContainsLexicalThisInComputedPropertyName = 1 << 18, - ContainsDefaultValueAssignments = 1 << 19, - ContainsParameterPropertyAssignments = 1 << 20, - ContainsSpread = 1 << 21, + ContainsDecorators = 1 << 12, + ContainsPropertyInitializer = 1 << 13, + ContainsLexicalThis = 1 << 14, + ContainsCapturedLexicalThis = 1 << 15, + ContainsLexicalThisInComputedPropertyName = 1 << 16, + ContainsDefaultValueAssignments = 1 << 17, + ContainsParameterPropertyAssignments = 1 << 18, + ContainsSpread = 1 << 19, + ContainsObjectSpread = 1 << 20, ContainsRest = ContainsSpread, - ContainsObjectSpread = 1 << 22, ContainsObjectRest = ContainsObjectSpread, - ContainsComputedPropertyName = 1 << 23, - ContainsBlockScopedBinding = 1 << 24, - ContainsBindingPattern = 1 << 25, - ContainsYield = 1 << 26, - ContainsHoistedDeclarationOrCompletion = 1 << 27, + ContainsComputedPropertyName = 1 << 21, + ContainsBlockScopedBinding = 1 << 22, + ContainsBindingPattern = 1 << 23, + ContainsYield = 1 << 24, + ContainsHoistedDeclarationOrCompletion = 1 << 25, HasComputedFlags = 1 << 29, // Transform flags have been computed. @@ -3595,9 +3593,9 @@ namespace ts { // - Bitmasks that are used to assert facts about the syntax of a node and its subtree. AssertTypeScript = TypeScript | ContainsTypeScript, AssertJsx = ContainsJsx, - AssertESNext = ESNext | ContainsESNext, + AssertESNext = ContainsESNext, AssertES2017 = ContainsES2017, - AssertES2016 = ES2016 | ContainsES2016, + AssertES2016 = ContainsES2016, AssertES2015 = ES2015 | ContainsES2015, AssertGenerator = Generator | ContainsGenerator, AssertDestructuringAssignment = DestructuringAssignment | ContainsDestructuringAssignment, @@ -3605,7 +3603,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 | ESNext | ES2016 | ES2015 | DestructuringAssignment | Generator | HasComputedFlags, + NodeExcludes = TypeScript | ES2015 | DestructuringAssignment | Generator | HasComputedFlags, ArrowFunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, FunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, ConstructorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, diff --git a/tests/baselines/reference/objectRestForOf.js b/tests/baselines/reference/objectRestForOf.js index 26ebd8f8229..f8872ffbae4 100644 --- a/tests/baselines/reference/objectRestForOf.js +++ b/tests/baselines/reference/objectRestForOf.js @@ -30,14 +30,14 @@ var __rest = (this && this.__rest) || function (s, e) { return t; }; let array; -for (var array_1 of array) { - var { x } = array_1, restOf = __rest(array_1, ["x"]); +for (let _a of array) { + let { x } = _a, restOf = __rest(_a, ["x"]); [x, restOf]; } let xx; let rrestOff; -for (var array_2 of array) { - ({ x: xx } = array_2, rrestOff = __rest(array_2, ["x"])); +for (let _b of array) { + ({ x: xx } = _b, rrestOff = __rest(_b, ["x"])); [xx, rrestOff]; } for (const norest of array.map(a => (__assign({}, a, { x: 'a string' })))) { diff --git a/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js b/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js index 8355d61d9c3..9f7a70699bd 100644 --- a/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js +++ b/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js @@ -194,13 +194,13 @@ for (_b = getRobot(), _c = _b.name, nameA = _c === void 0 ? "noName" : _c, _b, i for (_d = { name: "trimmer", skill: "trimming" }, _e = _d.name, nameA = _e === void 0 ? "noName" : _e, _d, i = 0; i < 1; i++) { console.log(nameA); } -for (_f = multiRobot.skills, _g = _f === void 0 ? { primary: "none", secondary: "none" } : _f, _h = _g.primary, primaryA = _h === void 0 ? "primary" : _h, _j = _g.secondary, secondaryA = _j === void 0 ? "secondary" : _j, multiRobot, multiRobot, i = 0; i < 1; i++) { +for (_f = multiRobot.skills, _g = _f === void 0 ? { primary: "none", secondary: "none" } : _f, _h = _g.primary, primaryA = _h === void 0 ? "primary" : _h, _j = _g.secondary, secondaryA = _j === void 0 ? "secondary" : _j, multiRobot, i = 0; i < 1; i++) { console.log(primaryA); } -for (_k = getMultiRobot(), (_l = _k.skills, _m = _l === void 0 ? { primary: "none", secondary: "none" } : _l, _o = _m.primary, primaryA = _o === void 0 ? "primary" : _o, _p = _m.secondary, secondaryA = _p === void 0 ? "secondary" : _p, _k), _k, i = 0; i < 1; i++) { +for (_k = getMultiRobot(), _l = _k.skills, _m = _l === void 0 ? { primary: "none", secondary: "none" } : _l, _o = _m.primary, primaryA = _o === void 0 ? "primary" : _o, _p = _m.secondary, secondaryA = _p === void 0 ? "secondary" : _p, _k, i = 0; i < 1; i++) { console.log(primaryA); } -for (_q = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, (_r = _q.skills, _s = _r === void 0 ? { primary: "none", secondary: "none" } : _r, _t = _s.primary, primaryA = _t === void 0 ? "primary" : _t, _u = _s.secondary, secondaryA = _u === void 0 ? "secondary" : _u, _q), _q, +for (_q = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, _r = _q.skills, _s = _r === void 0 ? { primary: "none", secondary: "none" } : _r, _t = _s.primary, primaryA = _t === void 0 ? "primary" : _t, _u = _s.secondary, secondaryA = _u === void 0 ? "secondary" : _u, _q, i = 0; i < 1; i++) { console.log(primaryA); } @@ -213,13 +213,13 @@ for (_w = getRobot(), _x = _w.name, name = _x === void 0 ? "noName" : _x, _w, i for (_y = { name: "trimmer", skill: "trimming" }, _z = _y.name, name = _z === void 0 ? "noName" : _z, _y, i = 0; i < 1; i++) { console.log(nameA); } -for (_0 = multiRobot.skills, _1 = _0 === void 0 ? { primary: "none", secondary: "none" } : _0, _2 = _1.primary, primary = _2 === void 0 ? "primary" : _2, _3 = _1.secondary, secondary = _3 === void 0 ? "secondary" : _3, multiRobot, multiRobot, i = 0; i < 1; i++) { +for (_0 = multiRobot.skills, _1 = _0 === void 0 ? { primary: "none", secondary: "none" } : _0, _2 = _1.primary, primary = _2 === void 0 ? "primary" : _2, _3 = _1.secondary, secondary = _3 === void 0 ? "secondary" : _3, multiRobot, i = 0; i < 1; i++) { console.log(primaryA); } -for (_4 = getMultiRobot(), (_5 = _4.skills, _6 = _5 === void 0 ? { primary: "none", secondary: "none" } : _5, _7 = _6.primary, primary = _7 === void 0 ? "primary" : _7, _8 = _6.secondary, secondary = _8 === void 0 ? "secondary" : _8, _4), _4, i = 0; i < 1; i++) { +for (_4 = getMultiRobot(), _5 = _4.skills, _6 = _5 === void 0 ? { primary: "none", secondary: "none" } : _5, _7 = _6.primary, primary = _7 === void 0 ? "primary" : _7, _8 = _6.secondary, secondary = _8 === void 0 ? "secondary" : _8, _4, i = 0; i < 1; i++) { console.log(primaryA); } -for (_9 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, (_10 = _9.skills, _11 = _10 === void 0 ? { primary: "none", secondary: "none" } : _10, _12 = _11.primary, primary = _12 === void 0 ? "primary" : _12, _13 = _11.secondary, secondary = _13 === void 0 ? "secondary" : _13, _9), _9, +for (_9 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, _10 = _9.skills, _11 = _10 === void 0 ? { primary: "none", secondary: "none" } : _10, _12 = _11.primary, primary = _12 === void 0 ? "primary" : _12, _13 = _11.secondary, secondary = _13 === void 0 ? "secondary" : _13, _9, i = 0; i < 1; i++) { console.log(primaryA); } @@ -232,13 +232,13 @@ for (_16 = getRobot(), _17 = _16.name, nameA = _17 === void 0 ? "noName" : _17, for (_19 = { name: "trimmer", skill: "trimming" }, _20 = _19.name, nameA = _20 === void 0 ? "noName" : _20, _21 = _19.skill, skillA = _21 === void 0 ? "skill" : _21, _19, i = 0; i < 1; i++) { console.log(nameA); } -for (_22 = multiRobot.name, nameA = _22 === void 0 ? "noName" : _22, _23 = multiRobot.skills, _24 = _23 === void 0 ? { primary: "none", secondary: "none" } : _23, _25 = _24.primary, primaryA = _25 === void 0 ? "primary" : _25, _26 = _24.secondary, secondaryA = _26 === void 0 ? "secondary" : _26, multiRobot, multiRobot, i = 0; i < 1; i++) { +for (_22 = multiRobot.name, nameA = _22 === void 0 ? "noName" : _22, _23 = multiRobot.skills, _24 = _23 === void 0 ? { primary: "none", secondary: "none" } : _23, _25 = _24.primary, primaryA = _25 === void 0 ? "primary" : _25, _26 = _24.secondary, secondaryA = _26 === void 0 ? "secondary" : _26, multiRobot, i = 0; i < 1; i++) { console.log(primaryA); } -for (_27 = getMultiRobot(), (_28 = _27.name, nameA = _28 === void 0 ? "noName" : _28, _29 = _27.skills, _30 = _29 === void 0 ? { primary: "none", secondary: "none" } : _29, _31 = _30.primary, primaryA = _31 === void 0 ? "primary" : _31, _32 = _30.secondary, secondaryA = _32 === void 0 ? "secondary" : _32, _27), _27, i = 0; i < 1; i++) { +for (_27 = getMultiRobot(), _28 = _27.name, nameA = _28 === void 0 ? "noName" : _28, _29 = _27.skills, _30 = _29 === void 0 ? { primary: "none", secondary: "none" } : _29, _31 = _30.primary, primaryA = _31 === void 0 ? "primary" : _31, _32 = _30.secondary, secondaryA = _32 === void 0 ? "secondary" : _32, _27, i = 0; i < 1; i++) { console.log(primaryA); } -for (_33 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, (_34 = _33.name, nameA = _34 === void 0 ? "noName" : _34, _35 = _33.skills, _36 = _35 === void 0 ? { primary: "none", secondary: "none" } : _35, _37 = _36.primary, primaryA = _37 === void 0 ? "primary" : _37, _38 = _36.secondary, secondaryA = _38 === void 0 ? "secondary" : _38, _33), _33, +for (_33 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, _34 = _33.name, nameA = _34 === void 0 ? "noName" : _34, _35 = _33.skills, _36 = _35 === void 0 ? { primary: "none", secondary: "none" } : _35, _37 = _36.primary, primaryA = _37 === void 0 ? "primary" : _37, _38 = _36.secondary, secondaryA = _38 === void 0 ? "secondary" : _38, _33, i = 0; i < 1; i++) { console.log(primaryA); } @@ -251,16 +251,15 @@ for (_41 = getRobot(), _42 = _41.name, name = _42 === void 0 ? "noName" : _42, _ for (_44 = { name: "trimmer", skill: "trimming" }, _45 = _44.name, name = _45 === void 0 ? "noName" : _45, _46 = _44.skill, skill = _46 === void 0 ? "skill" : _46, _44, i = 0; i < 1; i++) { console.log(nameA); } -for (_47 = multiRobot.name, name = _47 === void 0 ? "noName" : _47, _48 = multiRobot.skills, _49 = _48 === void 0 ? { primary: "none", secondary: "none" } : _48, _50 = _49.primary, primary = _50 === void 0 ? "primary" : _50, _51 = _49.secondary, secondary = _51 === void 0 ? "secondary" : _51, multiRobot, multiRobot, i = 0; i < 1; i++) { +for (_47 = multiRobot.name, name = _47 === void 0 ? "noName" : _47, _48 = multiRobot.skills, _49 = _48 === void 0 ? { primary: "none", secondary: "none" } : _48, _50 = _49.primary, primary = _50 === void 0 ? "primary" : _50, _51 = _49.secondary, secondary = _51 === void 0 ? "secondary" : _51, multiRobot, i = 0; i < 1; i++) { console.log(primaryA); } -for (_52 = getMultiRobot(), (_53 = _52.name, name = _53 === void 0 ? "noName" : _53, _54 = _52.skills, _55 = _54 === void 0 ? { primary: "none", secondary: "none" } : _54, _56 = _55.primary, primary = _56 === void 0 ? "primary" : _56, _57 = _55.secondary, secondary = _57 === void 0 ? "secondary" : _57, _52), _52, i = 0; i < 1; i++) { +for (_52 = getMultiRobot(), _53 = _52.name, name = _53 === void 0 ? "noName" : _53, _54 = _52.skills, _55 = _54 === void 0 ? { primary: "none", secondary: "none" } : _54, _56 = _55.primary, primary = _56 === void 0 ? "primary" : _56, _57 = _55.secondary, secondary = _57 === void 0 ? "secondary" : _57, _52, i = 0; i < 1; i++) { console.log(primaryA); } -for (_58 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, (_59 = _58.name, name = _59 === void 0 ? "noName" : _59, _60 = _58.skills, _61 = _60 === void 0 ? { primary: "none", secondary: "none" } : _60, _62 = _61.primary, primary = _62 === void 0 ? "primary" : _62, _63 = _61.secondary, secondary = _63 === void 0 ? "secondary" : _63, _58), _58, +for (_58 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, _59 = _58.name, name = _59 === void 0 ? "noName" : _59, _60 = _58.skills, _61 = _60 === void 0 ? { primary: "none", secondary: "none" } : _60, _62 = _61.primary, primary = _62 === void 0 ? "primary" : _62, _63 = _61.secondary, secondary = _63 === void 0 ? "secondary" : _63, _58, i = 0; i < 1; i++) { console.log(primaryA); } -var _k, _q, _4, _9, _27, _33, _52, _58; -var _a, _b, _c, _d, _e, _f, _g, _h, _j, _l, _m, _o, _p, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _5, _6, _7, _8, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _28, _29, _30, _31, _32, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _53, _54, _55, _56, _57, _59, _60, _61, _62, _63; +var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; //# sourceMappingURL=sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js.map \ No newline at end of file diff --git a/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js.map b/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js.map index 56f8aa0f607..dd248e89944 100644 --- a/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js.map +++ b/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js.map @@ -1,2 +1,2 @@ //// [sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js.map] -{"version":3,"file":"sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js","sourceRoot":"","sources":["sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.ts"],"names":[],"mappings":"AAgBA,IAAI,KAAK,GAAU,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AACtD,IAAI,UAAU,GAAe,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;AACjG;IACI,MAAM,CAAC,KAAK,CAAC;AACjB,CAAC;AACD;IACI,MAAM,CAAC,UAAU,CAAC;AACtB,CAAC;AAED,IAAI,KAAa,EAAE,QAAgB,EAAE,UAAkB,EAAE,CAAS,EAAE,MAAc,CAAC;AACnF,IAAI,IAAY,EAAE,OAAe,EAAE,SAAiB,EAAE,KAAa,CAAC;AAEpE,GAAG,CAAC,CAAE,eAAsB,EAAtB,qCAAsB,EAAK,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,eAAsC,EAArC,YAAsB,EAAtB,qCAAsB,MAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,2CAAyE,EAAxE,YAAsB,EAAtB,qCAAsB,MAAoD,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAChG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CACA,sBAG0C,EAH1C,gEAG0C,EAFtC,eAA6B,EAA7B,yCAA6B,EAC7B,iBAAmC,EAAnC,6CAAmC,EAEvC,UAAU,EAAV,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,oBAKc,GAJf,cAG0C,EAH1C,gEAG0C,EAFtC,eAA6B,EAA7B,yCAA6B,EAC7B,iBAAmC,EAAnC,6CAAmC,WAEtB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,8EAKoF,GAJrF,cAG0C,EAH1C,gEAG0C,EAFtC,eAA6B,EAA7B,yCAA6B,EAC7B,iBAAmC,EAAnC,6CAAmC;IAGvC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAED,GAAG,CAAC,CAAG,eAAe,EAAf,oCAAe,EAAK,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,eAAgC,EAA9B,YAAe,EAAf,oCAAe,MAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,2CAAmE,EAAjE,YAAe,EAAf,oCAAe,MAAoD,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAC1F,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CACA,sBAG0C,EAH1C,gEAG0C,EAFtC,eAAmB,EAAnB,wCAAmB,EACnB,iBAAuB,EAAvB,4CAAuB,EAE3B,UAAU,EAAV,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,oBAKc,GAJf,cAG0C,EAH1C,gEAG0C,EAFtC,eAAmB,EAAnB,wCAAmB,EACnB,iBAAuB,EAAvB,4CAAuB,WAEV,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,8EAKoF,GAJrF,eAG0C,EAH1C,mEAG0C,EAFtC,iBAAmB,EAAnB,0CAAmB,EACnB,mBAAuB,EAAvB,8CAAuB;IAG3B,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAGD,GAAG,CAAC,CAAE,gBAAsB,EAAtB,uCAAsB,EAAE,iBAAuB,EAAvB,uCAAuB,EAAK,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACjF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,gBAA+D,EAA9D,cAAsB,EAAtB,uCAAsB,EAAE,eAAuB,EAAvB,uCAAuB,OAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACtF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,4CAAkG,EAAjG,cAAsB,EAAtB,uCAAsB,EAAE,eAAuB,EAAvB,uCAAuB,OAAoD,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACzH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CACA,qBAAsB,EAAtB,uCAAsB,EACtB,uBAG0C,EAH1C,mEAG0C,EAFtC,iBAA6B,EAA7B,2CAA6B,EAC7B,mBAAmC,EAAnC,+CAAmC,EAEvC,UAAU,EAAV,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,qBAMc,GALf,cAAsB,EAAtB,uCAAsB,EACtB,gBAG0C,EAH1C,mEAG0C,EAFtC,iBAA6B,EAA7B,2CAA6B,EAC7B,mBAAmC,EAAnC,+CAAmC,aAEtB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,+EAMoF,GALrF,cAAsB,EAAtB,uCAAsB,EACtB,gBAG0C,EAH1C,mEAG0C,EAFtC,iBAA6B,EAA7B,2CAA6B,EAC7B,mBAAmC,EAAnC,+CAAmC;IAGvC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAED,GAAG,CAAC,CAAG,gBAAe,EAAf,sCAAe,EAAE,iBAAe,EAAf,sCAAe,EAAK,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACnE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,gBAAiD,EAA/C,cAAe,EAAf,sCAAe,EAAE,eAAe,EAAf,sCAAe,OAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,4CAAoF,EAAlF,cAAe,EAAf,sCAAe,EAAE,eAAe,EAAf,sCAAe,OAAoD,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAC3G,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CACA,qBAAe,EAAf,sCAAe,EACf,uBAG0C,EAH1C,mEAG0C,EAFtC,iBAAmB,EAAnB,0CAAmB,EACnB,mBAAuB,EAAvB,8CAAuB,EAE3B,UAAU,EAAV,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,qBAMc,GALf,cAAe,EAAf,sCAAe,EACf,gBAG0C,EAH1C,mEAG0C,EAFtC,iBAAmB,EAAnB,0CAAmB,EACnB,mBAAuB,EAAvB,8CAAuB,aAEV,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,+EAMoF,GALrF,cAAe,EAAf,sCAAe,EACf,gBAG0C,EAH1C,mEAG0C,EAFtC,iBAAmB,EAAnB,0CAAmB,EACnB,mBAAuB,EAAvB,8CAAuB;IAG3B,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC"} \ No newline at end of file +{"version":3,"file":"sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js","sourceRoot":"","sources":["sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.ts"],"names":[],"mappings":"AAgBA,IAAI,KAAK,GAAU,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AACtD,IAAI,UAAU,GAAe,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;AACjG;IACI,MAAM,CAAC,KAAK,CAAC;AACjB,CAAC;AACD;IACI,MAAM,CAAC,UAAU,CAAC;AACtB,CAAC;AAED,IAAI,KAAa,EAAE,QAAgB,EAAE,UAAkB,EAAE,CAAS,EAAE,MAAc,CAAC;AACnF,IAAI,IAAY,EAAE,OAAe,EAAE,SAAiB,EAAE,KAAa,CAAC;AAEpE,GAAG,CAAC,CAAE,eAAsB,EAAtB,qCAAsB,EAAK,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,eAAsC,EAArC,YAAsB,EAAtB,qCAAsB,MAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,2CAAyE,EAAxE,YAAsB,EAAtB,qCAAsB,MAAoD,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAChG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CACA,sBAG0C,EAH1C,gEAG0C,EAFtC,eAA6B,EAA7B,yCAA6B,EAC7B,iBAAmC,EAAnC,6CAAmC,EAEvC,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,oBAKc,EAJf,cAG0C,EAH1C,gEAG0C,EAFtC,eAA6B,EAA7B,yCAA6B,EAC7B,iBAAmC,EAAnC,6CAAmC,MAEtB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,8EAKoF,EAJrF,cAG0C,EAH1C,gEAG0C,EAFtC,eAA6B,EAA7B,yCAA6B,EAC7B,iBAAmC,EAAnC,6CAAmC;IAGvC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAED,GAAG,CAAC,CAAG,eAAe,EAAf,oCAAe,EAAK,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,eAAgC,EAA9B,YAAe,EAAf,oCAAe,MAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,2CAAmE,EAAjE,YAAe,EAAf,oCAAe,MAAoD,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAC1F,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CACA,sBAG0C,EAH1C,gEAG0C,EAFtC,eAAmB,EAAnB,wCAAmB,EACnB,iBAAuB,EAAvB,4CAAuB,EAE3B,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,oBAKc,EAJf,cAG0C,EAH1C,gEAG0C,EAFtC,eAAmB,EAAnB,wCAAmB,EACnB,iBAAuB,EAAvB,4CAAuB,MAEV,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,8EAKoF,EAJrF,eAG0C,EAH1C,mEAG0C,EAFtC,iBAAmB,EAAnB,0CAAmB,EACnB,mBAAuB,EAAvB,8CAAuB;IAG3B,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAGD,GAAG,CAAC,CAAE,gBAAsB,EAAtB,uCAAsB,EAAE,iBAAuB,EAAvB,uCAAuB,EAAK,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACjF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,gBAA+D,EAA9D,cAAsB,EAAtB,uCAAsB,EAAE,eAAuB,EAAvB,uCAAuB,OAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACtF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,4CAAkG,EAAjG,cAAsB,EAAtB,uCAAsB,EAAE,eAAuB,EAAvB,uCAAuB,OAAoD,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACzH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CACA,qBAAsB,EAAtB,uCAAsB,EACtB,uBAG0C,EAH1C,mEAG0C,EAFtC,iBAA6B,EAA7B,2CAA6B,EAC7B,mBAAmC,EAAnC,+CAAmC,EAEvC,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,qBAMc,EALf,cAAsB,EAAtB,uCAAsB,EACtB,gBAG0C,EAH1C,mEAG0C,EAFtC,iBAA6B,EAA7B,2CAA6B,EAC7B,mBAAmC,EAAnC,+CAAmC,OAEtB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,+EAMoF,EALrF,cAAsB,EAAtB,uCAAsB,EACtB,gBAG0C,EAH1C,mEAG0C,EAFtC,iBAA6B,EAA7B,2CAA6B,EAC7B,mBAAmC,EAAnC,+CAAmC;IAGvC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAED,GAAG,CAAC,CAAG,gBAAe,EAAf,sCAAe,EAAE,iBAAe,EAAf,sCAAe,EAAK,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACnE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,gBAAiD,EAA/C,cAAe,EAAf,sCAAe,EAAE,eAAe,EAAf,sCAAe,OAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CAAC,4CAAoF,EAAlF,cAAe,EAAf,sCAAe,EAAE,eAAe,EAAf,sCAAe,OAAoD,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAC3G,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,GAAG,CAAC,CACA,qBAAe,EAAf,sCAAe,EACf,uBAG0C,EAH1C,mEAG0C,EAFtC,iBAAmB,EAAnB,0CAAmB,EACnB,mBAAuB,EAAvB,8CAAuB,EAE3B,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,qBAMc,EALf,cAAe,EAAf,sCAAe,EACf,gBAG0C,EAH1C,mEAG0C,EAFtC,iBAAmB,EAAnB,0CAAmB,EACnB,mBAAuB,EAAvB,8CAAuB,OAEV,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AACD,GAAG,CAAC,CAAC,+EAMoF,EALrF,cAAe,EAAf,sCAAe,EACf,gBAG0C,EAH1C,mEAG0C,EAFtC,iBAAmB,EAAnB,0CAAmB,EACnB,mBAAuB,EAAvB,8CAAuB;IAG3B,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.sourcemap.txt b/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.sourcemap.txt index 9e0e92d4a68..97271e8f3d0 100644 --- a/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.sourcemap.txt +++ b/tests/baselines/reference/sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.sourcemap.txt @@ -594,14 +594,14 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(19, 1) Source(37, 1) + SourceIndex(0) 2 >Emitted(19, 2) Source(37, 2) + SourceIndex(0) --- ->>>for (_f = multiRobot.skills, _g = _f === void 0 ? { primary: "none", secondary: "none" } : _f, _h = _g.primary, primaryA = _h === void 0 ? "primary" : _h, _j = _g.secondary, secondaryA = _j === void 0 ? "secondary" : _j, multiRobot, multiRobot, i = 0; i < 1; i++) { +>>>for (_f = multiRobot.skills, _g = _f === void 0 ? { primary: "none", secondary: "none" } : _f, _h = _g.primary, primaryA = _h === void 0 ? "primary" : _h, _j = _g.secondary, secondaryA = _j === void 0 ? "secondary" : _j, multiRobot, i = 0; i < 1; i++) { 1-> 2 >^^^ 3 > ^ @@ -620,20 +620,18 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 16> ^^ 17> ^^^^^^^^^^ 18> ^^ -19> ^^^^^^^^^^ -20> ^^ -21> ^ -22> ^^^ -23> ^ -24> ^^ -25> ^ -26> ^^^ -27> ^ -28> ^^ -29> ^ -30> ^^ -31> ^^ -32> ^ +19> ^ +20> ^^^ +21> ^ +22> ^^ +23> ^ +24> ^^^ +25> ^ +26> ^^ +27> ^ +28> ^^ +29> ^^ +30> ^ 1-> > 2 >for @@ -662,21 +660,19 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = 17> multiRobot -18> -19> multiRobot -20> , -21> i -22> = -23> 0 -24> ; -25> i -26> < -27> 1 -28> ; -29> i -30> ++ -31> ) -32> { +18> , +19> i +20> = +21> 0 +22> ; +23> i +24> < +25> 1 +26> ; +27> i +28> ++ +29> ) +30> { 1->Emitted(20, 1) Source(38, 1) + SourceIndex(0) 2 >Emitted(20, 4) Source(38, 4) + SourceIndex(0) 3 >Emitted(20, 5) Source(38, 5) + SourceIndex(0) @@ -694,21 +690,19 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 15>Emitted(20, 220) Source(41, 44) + SourceIndex(0) 16>Emitted(20, 222) Source(43, 5) + SourceIndex(0) 17>Emitted(20, 232) Source(43, 15) + SourceIndex(0) -18>Emitted(20, 234) Source(43, 5) + SourceIndex(0) -19>Emitted(20, 244) Source(43, 15) + SourceIndex(0) -20>Emitted(20, 246) Source(43, 17) + SourceIndex(0) -21>Emitted(20, 247) Source(43, 18) + SourceIndex(0) -22>Emitted(20, 250) Source(43, 21) + SourceIndex(0) -23>Emitted(20, 251) Source(43, 22) + SourceIndex(0) -24>Emitted(20, 253) Source(43, 24) + SourceIndex(0) -25>Emitted(20, 254) Source(43, 25) + SourceIndex(0) -26>Emitted(20, 257) Source(43, 28) + SourceIndex(0) -27>Emitted(20, 258) Source(43, 29) + SourceIndex(0) -28>Emitted(20, 260) Source(43, 31) + SourceIndex(0) -29>Emitted(20, 261) Source(43, 32) + SourceIndex(0) -30>Emitted(20, 263) Source(43, 34) + SourceIndex(0) -31>Emitted(20, 265) Source(43, 36) + SourceIndex(0) -32>Emitted(20, 266) Source(43, 37) + SourceIndex(0) +18>Emitted(20, 234) Source(43, 17) + SourceIndex(0) +19>Emitted(20, 235) Source(43, 18) + SourceIndex(0) +20>Emitted(20, 238) Source(43, 21) + SourceIndex(0) +21>Emitted(20, 239) Source(43, 22) + SourceIndex(0) +22>Emitted(20, 241) Source(43, 24) + SourceIndex(0) +23>Emitted(20, 242) Source(43, 25) + SourceIndex(0) +24>Emitted(20, 245) Source(43, 28) + SourceIndex(0) +25>Emitted(20, 246) Source(43, 29) + SourceIndex(0) +26>Emitted(20, 248) Source(43, 31) + SourceIndex(0) +27>Emitted(20, 249) Source(43, 32) + SourceIndex(0) +28>Emitted(20, 251) Source(43, 34) + SourceIndex(0) +29>Emitted(20, 253) Source(43, 36) + SourceIndex(0) +30>Emitted(20, 254) Source(43, 37) + SourceIndex(0) --- >>> console.log(primaryA); 1 >^^^^ @@ -740,44 +734,44 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(22, 1) Source(45, 1) + SourceIndex(0) 2 >Emitted(22, 2) Source(45, 2) + SourceIndex(0) --- ->>>for (_k = getMultiRobot(), (_l = _k.skills, _m = _l === void 0 ? { primary: "none", secondary: "none" } : _l, _o = _m.primary, primaryA = _o === void 0 ? "primary" : _o, _p = _m.secondary, secondaryA = _p === void 0 ? "secondary" : _p, _k), _k, i = 0; i < 1; i++) { +>>>for (_k = getMultiRobot(), _l = _k.skills, _m = _l === void 0 ? { primary: "none", secondary: "none" } : _l, _o = _m.primary, primaryA = _o === void 0 ? "primary" : _o, _p = _m.secondary, secondaryA = _p === void 0 ? "secondary" : _p, _k, i = 0; i < 1; i++) { 1-> 2 >^^^ 3 > ^ 4 > ^ 5 > ^^^^^^^^^^^^^^^^^^^^ -6 > ^^^ -7 > ^^^^^^^^^^^^^^ -8 > ^^ -9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -10> ^^ -11> ^^^^^^^^^^^^^^^ -12> ^^ -13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -14> ^^ -15> ^^^^^^^^^^^^^^^^^ -16> ^^ -17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -18> ^^^^^^^^^^^ -19> ^ -20> ^^^ -21> ^ -22> ^^ -23> ^ -24> ^^^ -25> ^ -26> ^^ -27> ^ -28> ^^ -29> ^^ -30> ^ +6 > ^^ +7 > ^^^^^^^^^^^^^^ +8 > ^^ +9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10> ^^ +11> ^^^^^^^^^^^^^^^ +12> ^^ +13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +14> ^^ +15> ^^^^^^^^^^^^^^^^^ +16> ^^ +17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +18> ^^^^^^ +19> ^ +20> ^^^ +21> ^ +22> ^^ +23> ^ +24> ^^^ +25> ^ +26> ^^ +27> ^ +28> ^^ +29> ^^ +30> ^ 1-> > 2 >for @@ -790,69 +784,69 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = getMultiRobot() 6 > -7 > skills: { - > primary: primaryA = "primary", - > secondary: secondaryA = "secondary" - > } = { primary: "none", secondary: "none" } -8 > -9 > skills: { - > primary: primaryA = "primary", - > secondary: secondaryA = "secondary" - > } = { primary: "none", secondary: "none" } -10> -11> primary: primaryA = "primary" -12> -13> primary: primaryA = "primary" -14> , - > -15> secondary: secondaryA = "secondary" -16> -17> secondary: secondaryA = "secondary" -18> - > } = { primary: "none", secondary: "none" } - > } = getMultiRobot(), -19> i -20> = -21> 0 -22> ; -23> i -24> < -25> 1 -26> ; -27> i -28> ++ -29> ) -30> { +7 > skills: { + > primary: primaryA = "primary", + > secondary: secondaryA = "secondary" + > } = { primary: "none", secondary: "none" } +8 > +9 > skills: { + > primary: primaryA = "primary", + > secondary: secondaryA = "secondary" + > } = { primary: "none", secondary: "none" } +10> +11> primary: primaryA = "primary" +12> +13> primary: primaryA = "primary" +14> , + > +15> secondary: secondaryA = "secondary" +16> +17> secondary: secondaryA = "secondary" +18> + > } = { primary: "none", secondary: "none" } + > } = getMultiRobot(), +19> i +20> = +21> 0 +22> ; +23> i +24> < +25> 1 +26> ; +27> i +28> ++ +29> ) +30> { 1->Emitted(23, 1) Source(46, 1) + SourceIndex(0) 2 >Emitted(23, 4) Source(46, 4) + SourceIndex(0) 3 >Emitted(23, 5) Source(46, 5) + SourceIndex(0) 4 >Emitted(23, 6) Source(46, 6) + SourceIndex(0) 5 >Emitted(23, 26) Source(51, 20) + SourceIndex(0) -6 >Emitted(23, 29) Source(47, 5) + SourceIndex(0) -7 >Emitted(23, 43) Source(50, 47) + SourceIndex(0) -8 >Emitted(23, 45) Source(47, 5) + SourceIndex(0) -9 >Emitted(23, 109) Source(50, 47) + SourceIndex(0) -10>Emitted(23, 111) Source(48, 9) + SourceIndex(0) -11>Emitted(23, 126) Source(48, 38) + SourceIndex(0) -12>Emitted(23, 128) Source(48, 9) + SourceIndex(0) -13>Emitted(23, 169) Source(48, 38) + SourceIndex(0) -14>Emitted(23, 171) Source(49, 9) + SourceIndex(0) -15>Emitted(23, 188) Source(49, 44) + SourceIndex(0) -16>Emitted(23, 190) Source(49, 9) + SourceIndex(0) -17>Emitted(23, 235) Source(49, 44) + SourceIndex(0) -18>Emitted(23, 246) Source(51, 22) + SourceIndex(0) -19>Emitted(23, 247) Source(51, 23) + SourceIndex(0) -20>Emitted(23, 250) Source(51, 26) + SourceIndex(0) -21>Emitted(23, 251) Source(51, 27) + SourceIndex(0) -22>Emitted(23, 253) Source(51, 29) + SourceIndex(0) -23>Emitted(23, 254) Source(51, 30) + SourceIndex(0) -24>Emitted(23, 257) Source(51, 33) + SourceIndex(0) -25>Emitted(23, 258) Source(51, 34) + SourceIndex(0) -26>Emitted(23, 260) Source(51, 36) + SourceIndex(0) -27>Emitted(23, 261) Source(51, 37) + SourceIndex(0) -28>Emitted(23, 263) Source(51, 39) + SourceIndex(0) -29>Emitted(23, 265) Source(51, 41) + SourceIndex(0) -30>Emitted(23, 266) Source(51, 42) + SourceIndex(0) +6 >Emitted(23, 28) Source(47, 5) + SourceIndex(0) +7 >Emitted(23, 42) Source(50, 47) + SourceIndex(0) +8 >Emitted(23, 44) Source(47, 5) + SourceIndex(0) +9 >Emitted(23, 108) Source(50, 47) + SourceIndex(0) +10>Emitted(23, 110) Source(48, 9) + SourceIndex(0) +11>Emitted(23, 125) Source(48, 38) + SourceIndex(0) +12>Emitted(23, 127) Source(48, 9) + SourceIndex(0) +13>Emitted(23, 168) Source(48, 38) + SourceIndex(0) +14>Emitted(23, 170) Source(49, 9) + SourceIndex(0) +15>Emitted(23, 187) Source(49, 44) + SourceIndex(0) +16>Emitted(23, 189) Source(49, 9) + SourceIndex(0) +17>Emitted(23, 234) Source(49, 44) + SourceIndex(0) +18>Emitted(23, 240) Source(51, 22) + SourceIndex(0) +19>Emitted(23, 241) Source(51, 23) + SourceIndex(0) +20>Emitted(23, 244) Source(51, 26) + SourceIndex(0) +21>Emitted(23, 245) Source(51, 27) + SourceIndex(0) +22>Emitted(23, 247) Source(51, 29) + SourceIndex(0) +23>Emitted(23, 248) Source(51, 30) + SourceIndex(0) +24>Emitted(23, 251) Source(51, 33) + SourceIndex(0) +25>Emitted(23, 252) Source(51, 34) + SourceIndex(0) +26>Emitted(23, 254) Source(51, 36) + SourceIndex(0) +27>Emitted(23, 255) Source(51, 37) + SourceIndex(0) +28>Emitted(23, 257) Source(51, 39) + SourceIndex(0) +29>Emitted(23, 259) Source(51, 41) + SourceIndex(0) +30>Emitted(23, 260) Source(51, 42) + SourceIndex(0) --- >>> console.log(primaryA); 1 >^^^^ @@ -884,31 +878,31 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(25, 1) Source(53, 1) + SourceIndex(0) 2 >Emitted(25, 2) Source(53, 2) + SourceIndex(0) --- ->>>for (_q = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, (_r = _q.skills, _s = _r === void 0 ? { primary: "none", secondary: "none" } : _r, _t = _s.primary, primaryA = _t === void 0 ? "primary" : _t, _u = _s.secondary, secondaryA = _u === void 0 ? "secondary" : _u, _q), _q, +>>>for (_q = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, _r = _q.skills, _s = _r === void 0 ? { primary: "none", secondary: "none" } : _r, _t = _s.primary, primaryA = _t === void 0 ? "primary" : _t, _u = _s.secondary, secondaryA = _u === void 0 ? "secondary" : _u, _q, 1-> 2 >^^^ 3 > ^ 4 > ^ 5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -6 > ^^^ -7 > ^^^^^^^^^^^^^^ -8 > ^^ -9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -10> ^^ -11> ^^^^^^^^^^^^^^^ -12> ^^ -13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -14> ^^ -15> ^^^^^^^^^^^^^^^^^ -16> ^^ -17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 > ^^ +7 > ^^^^^^^^^^^^^^ +8 > ^^ +9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10> ^^ +11> ^^^^^^^^^^^^^^^ +12> ^^ +13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +14> ^^ +15> ^^^^^^^^^^^^^^^^^ +16> ^^ +17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1-> > 2 >for @@ -921,41 +915,41 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } } 6 > -7 > skills: { - > primary: primaryA = "primary", - > secondary: secondaryA = "secondary" - > } = { primary: "none", secondary: "none" } -8 > -9 > skills: { - > primary: primaryA = "primary", - > secondary: secondaryA = "secondary" - > } = { primary: "none", secondary: "none" } -10> -11> primary: primaryA = "primary" -12> -13> primary: primaryA = "primary" -14> , - > -15> secondary: secondaryA = "secondary" -16> -17> secondary: secondaryA = "secondary" +7 > skills: { + > primary: primaryA = "primary", + > secondary: secondaryA = "secondary" + > } = { primary: "none", secondary: "none" } +8 > +9 > skills: { + > primary: primaryA = "primary", + > secondary: secondaryA = "secondary" + > } = { primary: "none", secondary: "none" } +10> +11> primary: primaryA = "primary" +12> +13> primary: primaryA = "primary" +14> , + > +15> secondary: secondaryA = "secondary" +16> +17> secondary: secondaryA = "secondary" 1->Emitted(26, 1) Source(54, 1) + SourceIndex(0) 2 >Emitted(26, 4) Source(54, 4) + SourceIndex(0) 3 >Emitted(26, 5) Source(54, 5) + SourceIndex(0) 4 >Emitted(26, 6) Source(54, 6) + SourceIndex(0) 5 >Emitted(26, 84) Source(59, 90) + SourceIndex(0) -6 >Emitted(26, 87) Source(55, 5) + SourceIndex(0) -7 >Emitted(26, 101) Source(58, 47) + SourceIndex(0) -8 >Emitted(26, 103) Source(55, 5) + SourceIndex(0) -9 >Emitted(26, 167) Source(58, 47) + SourceIndex(0) -10>Emitted(26, 169) Source(56, 9) + SourceIndex(0) -11>Emitted(26, 184) Source(56, 38) + SourceIndex(0) -12>Emitted(26, 186) Source(56, 9) + SourceIndex(0) -13>Emitted(26, 227) Source(56, 38) + SourceIndex(0) -14>Emitted(26, 229) Source(57, 9) + SourceIndex(0) -15>Emitted(26, 246) Source(57, 44) + SourceIndex(0) -16>Emitted(26, 248) Source(57, 9) + SourceIndex(0) -17>Emitted(26, 293) Source(57, 44) + SourceIndex(0) +6 >Emitted(26, 86) Source(55, 5) + SourceIndex(0) +7 >Emitted(26, 100) Source(58, 47) + SourceIndex(0) +8 >Emitted(26, 102) Source(55, 5) + SourceIndex(0) +9 >Emitted(26, 166) Source(58, 47) + SourceIndex(0) +10>Emitted(26, 168) Source(56, 9) + SourceIndex(0) +11>Emitted(26, 183) Source(56, 38) + SourceIndex(0) +12>Emitted(26, 185) Source(56, 9) + SourceIndex(0) +13>Emitted(26, 226) Source(56, 38) + SourceIndex(0) +14>Emitted(26, 228) Source(57, 9) + SourceIndex(0) +15>Emitted(26, 245) Source(57, 44) + SourceIndex(0) +16>Emitted(26, 247) Source(57, 9) + SourceIndex(0) +17>Emitted(26, 292) Source(57, 44) + SourceIndex(0) --- >>> i = 0; i < 1; i++) { 1 >^^^^ @@ -1351,14 +1345,14 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(38, 1) Source(72, 1) + SourceIndex(0) 2 >Emitted(38, 2) Source(72, 2) + SourceIndex(0) --- ->>>for (_0 = multiRobot.skills, _1 = _0 === void 0 ? { primary: "none", secondary: "none" } : _0, _2 = _1.primary, primary = _2 === void 0 ? "primary" : _2, _3 = _1.secondary, secondary = _3 === void 0 ? "secondary" : _3, multiRobot, multiRobot, i = 0; i < 1; i++) { +>>>for (_0 = multiRobot.skills, _1 = _0 === void 0 ? { primary: "none", secondary: "none" } : _0, _2 = _1.primary, primary = _2 === void 0 ? "primary" : _2, _3 = _1.secondary, secondary = _3 === void 0 ? "secondary" : _3, multiRobot, i = 0; i < 1; i++) { 1-> 2 >^^^ 3 > ^ @@ -1377,20 +1371,18 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 16> ^^ 17> ^^^^^^^^^^ 18> ^^ -19> ^^^^^^^^^^ -20> ^^ -21> ^ -22> ^^^ -23> ^ -24> ^^ -25> ^ -26> ^^^ -27> ^ -28> ^^ -29> ^ -30> ^^ -31> ^^ -32> ^ +19> ^ +20> ^^^ +21> ^ +22> ^^ +23> ^ +24> ^^^ +25> ^ +26> ^^ +27> ^ +28> ^^ +29> ^^ +30> ^ 1-> > 2 >for @@ -1419,21 +1411,19 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = 17> multiRobot -18> -19> multiRobot -20> , -21> i -22> = -23> 0 -24> ; -25> i -26> < -27> 1 -28> ; -29> i -30> ++ -31> ) -32> { +18> , +19> i +20> = +21> 0 +22> ; +23> i +24> < +25> 1 +26> ; +27> i +28> ++ +29> ) +30> { 1->Emitted(39, 1) Source(73, 1) + SourceIndex(0) 2 >Emitted(39, 4) Source(73, 4) + SourceIndex(0) 3 >Emitted(39, 5) Source(73, 5) + SourceIndex(0) @@ -1451,21 +1441,19 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 15>Emitted(39, 218) Source(76, 32) + SourceIndex(0) 16>Emitted(39, 220) Source(78, 5) + SourceIndex(0) 17>Emitted(39, 230) Source(78, 15) + SourceIndex(0) -18>Emitted(39, 232) Source(78, 5) + SourceIndex(0) -19>Emitted(39, 242) Source(78, 15) + SourceIndex(0) -20>Emitted(39, 244) Source(78, 17) + SourceIndex(0) -21>Emitted(39, 245) Source(78, 18) + SourceIndex(0) -22>Emitted(39, 248) Source(78, 21) + SourceIndex(0) -23>Emitted(39, 249) Source(78, 22) + SourceIndex(0) -24>Emitted(39, 251) Source(78, 24) + SourceIndex(0) -25>Emitted(39, 252) Source(78, 25) + SourceIndex(0) -26>Emitted(39, 255) Source(78, 28) + SourceIndex(0) -27>Emitted(39, 256) Source(78, 29) + SourceIndex(0) -28>Emitted(39, 258) Source(78, 31) + SourceIndex(0) -29>Emitted(39, 259) Source(78, 32) + SourceIndex(0) -30>Emitted(39, 261) Source(78, 34) + SourceIndex(0) -31>Emitted(39, 263) Source(78, 36) + SourceIndex(0) -32>Emitted(39, 264) Source(78, 37) + SourceIndex(0) +18>Emitted(39, 232) Source(78, 17) + SourceIndex(0) +19>Emitted(39, 233) Source(78, 18) + SourceIndex(0) +20>Emitted(39, 236) Source(78, 21) + SourceIndex(0) +21>Emitted(39, 237) Source(78, 22) + SourceIndex(0) +22>Emitted(39, 239) Source(78, 24) + SourceIndex(0) +23>Emitted(39, 240) Source(78, 25) + SourceIndex(0) +24>Emitted(39, 243) Source(78, 28) + SourceIndex(0) +25>Emitted(39, 244) Source(78, 29) + SourceIndex(0) +26>Emitted(39, 246) Source(78, 31) + SourceIndex(0) +27>Emitted(39, 247) Source(78, 32) + SourceIndex(0) +28>Emitted(39, 249) Source(78, 34) + SourceIndex(0) +29>Emitted(39, 251) Source(78, 36) + SourceIndex(0) +30>Emitted(39, 252) Source(78, 37) + SourceIndex(0) --- >>> console.log(primaryA); 1 >^^^^ @@ -1497,44 +1485,44 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(41, 1) Source(80, 1) + SourceIndex(0) 2 >Emitted(41, 2) Source(80, 2) + SourceIndex(0) --- ->>>for (_4 = getMultiRobot(), (_5 = _4.skills, _6 = _5 === void 0 ? { primary: "none", secondary: "none" } : _5, _7 = _6.primary, primary = _7 === void 0 ? "primary" : _7, _8 = _6.secondary, secondary = _8 === void 0 ? "secondary" : _8, _4), _4, i = 0; i < 1; i++) { +>>>for (_4 = getMultiRobot(), _5 = _4.skills, _6 = _5 === void 0 ? { primary: "none", secondary: "none" } : _5, _7 = _6.primary, primary = _7 === void 0 ? "primary" : _7, _8 = _6.secondary, secondary = _8 === void 0 ? "secondary" : _8, _4, i = 0; i < 1; i++) { 1-> 2 >^^^ 3 > ^ 4 > ^ 5 > ^^^^^^^^^^^^^^^^^^^^ -6 > ^^^ -7 > ^^^^^^^^^^^^^^ -8 > ^^ -9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -10> ^^ -11> ^^^^^^^^^^^^^^^ -12> ^^ -13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -14> ^^ -15> ^^^^^^^^^^^^^^^^^ -16> ^^ -17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -18> ^^^^^^^^^^^ -19> ^ -20> ^^^ -21> ^ -22> ^^ -23> ^ -24> ^^^ -25> ^ -26> ^^ -27> ^ -28> ^^ -29> ^^ -30> ^ +6 > ^^ +7 > ^^^^^^^^^^^^^^ +8 > ^^ +9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10> ^^ +11> ^^^^^^^^^^^^^^^ +12> ^^ +13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +14> ^^ +15> ^^^^^^^^^^^^^^^^^ +16> ^^ +17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +18> ^^^^^^ +19> ^ +20> ^^^ +21> ^ +22> ^^ +23> ^ +24> ^^^ +25> ^ +26> ^^ +27> ^ +28> ^^ +29> ^^ +30> ^ 1-> > 2 >for @@ -1547,69 +1535,69 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = getMultiRobot() 6 > -7 > skills: { - > primary = "primary", - > secondary = "secondary" - > } = { primary: "none", secondary: "none" } -8 > -9 > skills: { - > primary = "primary", - > secondary = "secondary" - > } = { primary: "none", secondary: "none" } -10> -11> primary = "primary" -12> -13> primary = "primary" -14> , - > -15> secondary = "secondary" -16> -17> secondary = "secondary" -18> - > } = { primary: "none", secondary: "none" } - > } = getMultiRobot(), -19> i -20> = -21> 0 -22> ; -23> i -24> < -25> 1 -26> ; -27> i -28> ++ -29> ) -30> { +7 > skills: { + > primary = "primary", + > secondary = "secondary" + > } = { primary: "none", secondary: "none" } +8 > +9 > skills: { + > primary = "primary", + > secondary = "secondary" + > } = { primary: "none", secondary: "none" } +10> +11> primary = "primary" +12> +13> primary = "primary" +14> , + > +15> secondary = "secondary" +16> +17> secondary = "secondary" +18> + > } = { primary: "none", secondary: "none" } + > } = getMultiRobot(), +19> i +20> = +21> 0 +22> ; +23> i +24> < +25> 1 +26> ; +27> i +28> ++ +29> ) +30> { 1->Emitted(42, 1) Source(81, 1) + SourceIndex(0) 2 >Emitted(42, 4) Source(81, 4) + SourceIndex(0) 3 >Emitted(42, 5) Source(81, 5) + SourceIndex(0) 4 >Emitted(42, 6) Source(81, 6) + SourceIndex(0) 5 >Emitted(42, 26) Source(86, 20) + SourceIndex(0) -6 >Emitted(42, 29) Source(82, 5) + SourceIndex(0) -7 >Emitted(42, 43) Source(85, 47) + SourceIndex(0) -8 >Emitted(42, 45) Source(82, 5) + SourceIndex(0) -9 >Emitted(42, 109) Source(85, 47) + SourceIndex(0) -10>Emitted(42, 111) Source(83, 9) + SourceIndex(0) -11>Emitted(42, 126) Source(83, 28) + SourceIndex(0) -12>Emitted(42, 128) Source(83, 9) + SourceIndex(0) -13>Emitted(42, 168) Source(83, 28) + SourceIndex(0) -14>Emitted(42, 170) Source(84, 9) + SourceIndex(0) -15>Emitted(42, 187) Source(84, 32) + SourceIndex(0) -16>Emitted(42, 189) Source(84, 9) + SourceIndex(0) -17>Emitted(42, 233) Source(84, 32) + SourceIndex(0) -18>Emitted(42, 244) Source(86, 22) + SourceIndex(0) -19>Emitted(42, 245) Source(86, 23) + SourceIndex(0) -20>Emitted(42, 248) Source(86, 26) + SourceIndex(0) -21>Emitted(42, 249) Source(86, 27) + SourceIndex(0) -22>Emitted(42, 251) Source(86, 29) + SourceIndex(0) -23>Emitted(42, 252) Source(86, 30) + SourceIndex(0) -24>Emitted(42, 255) Source(86, 33) + SourceIndex(0) -25>Emitted(42, 256) Source(86, 34) + SourceIndex(0) -26>Emitted(42, 258) Source(86, 36) + SourceIndex(0) -27>Emitted(42, 259) Source(86, 37) + SourceIndex(0) -28>Emitted(42, 261) Source(86, 39) + SourceIndex(0) -29>Emitted(42, 263) Source(86, 41) + SourceIndex(0) -30>Emitted(42, 264) Source(86, 42) + SourceIndex(0) +6 >Emitted(42, 28) Source(82, 5) + SourceIndex(0) +7 >Emitted(42, 42) Source(85, 47) + SourceIndex(0) +8 >Emitted(42, 44) Source(82, 5) + SourceIndex(0) +9 >Emitted(42, 108) Source(85, 47) + SourceIndex(0) +10>Emitted(42, 110) Source(83, 9) + SourceIndex(0) +11>Emitted(42, 125) Source(83, 28) + SourceIndex(0) +12>Emitted(42, 127) Source(83, 9) + SourceIndex(0) +13>Emitted(42, 167) Source(83, 28) + SourceIndex(0) +14>Emitted(42, 169) Source(84, 9) + SourceIndex(0) +15>Emitted(42, 186) Source(84, 32) + SourceIndex(0) +16>Emitted(42, 188) Source(84, 9) + SourceIndex(0) +17>Emitted(42, 232) Source(84, 32) + SourceIndex(0) +18>Emitted(42, 238) Source(86, 22) + SourceIndex(0) +19>Emitted(42, 239) Source(86, 23) + SourceIndex(0) +20>Emitted(42, 242) Source(86, 26) + SourceIndex(0) +21>Emitted(42, 243) Source(86, 27) + SourceIndex(0) +22>Emitted(42, 245) Source(86, 29) + SourceIndex(0) +23>Emitted(42, 246) Source(86, 30) + SourceIndex(0) +24>Emitted(42, 249) Source(86, 33) + SourceIndex(0) +25>Emitted(42, 250) Source(86, 34) + SourceIndex(0) +26>Emitted(42, 252) Source(86, 36) + SourceIndex(0) +27>Emitted(42, 253) Source(86, 37) + SourceIndex(0) +28>Emitted(42, 255) Source(86, 39) + SourceIndex(0) +29>Emitted(42, 257) Source(86, 41) + SourceIndex(0) +30>Emitted(42, 258) Source(86, 42) + SourceIndex(0) --- >>> console.log(primaryA); 1 >^^^^ @@ -1641,31 +1629,31 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(44, 1) Source(88, 1) + SourceIndex(0) 2 >Emitted(44, 2) Source(88, 2) + SourceIndex(0) --- ->>>for (_9 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, (_10 = _9.skills, _11 = _10 === void 0 ? { primary: "none", secondary: "none" } : _10, _12 = _11.primary, primary = _12 === void 0 ? "primary" : _12, _13 = _11.secondary, secondary = _13 === void 0 ? "secondary" : _13, _9), _9, +>>>for (_9 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, _10 = _9.skills, _11 = _10 === void 0 ? { primary: "none", secondary: "none" } : _10, _12 = _11.primary, primary = _12 === void 0 ? "primary" : _12, _13 = _11.secondary, secondary = _13 === void 0 ? "secondary" : _13, _9, 1-> 2 >^^^ 3 > ^ 4 > ^ 5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -6 > ^^^ -7 > ^^^^^^^^^^^^^^^ -8 > ^^ -9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -10> ^^ -11> ^^^^^^^^^^^^^^^^^ -12> ^^ -13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -14> ^^ -15> ^^^^^^^^^^^^^^^^^^^ -16> ^^ -17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 > ^^ +7 > ^^^^^^^^^^^^^^^ +8 > ^^ +9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10> ^^ +11> ^^^^^^^^^^^^^^^^^ +12> ^^ +13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +14> ^^ +15> ^^^^^^^^^^^^^^^^^^^ +16> ^^ +17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1-> > 2 >for @@ -1678,41 +1666,41 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } } 6 > -7 > skills: { - > primary = "primary", - > secondary = "secondary" - > } = { primary: "none", secondary: "none" } -8 > -9 > skills: { - > primary = "primary", - > secondary = "secondary" - > } = { primary: "none", secondary: "none" } -10> -11> primary = "primary" -12> -13> primary = "primary" -14> , - > -15> secondary = "secondary" -16> -17> secondary = "secondary" +7 > skills: { + > primary = "primary", + > secondary = "secondary" + > } = { primary: "none", secondary: "none" } +8 > +9 > skills: { + > primary = "primary", + > secondary = "secondary" + > } = { primary: "none", secondary: "none" } +10> +11> primary = "primary" +12> +13> primary = "primary" +14> , + > +15> secondary = "secondary" +16> +17> secondary = "secondary" 1->Emitted(45, 1) Source(89, 1) + SourceIndex(0) 2 >Emitted(45, 4) Source(89, 4) + SourceIndex(0) 3 >Emitted(45, 5) Source(89, 5) + SourceIndex(0) 4 >Emitted(45, 6) Source(89, 6) + SourceIndex(0) 5 >Emitted(45, 84) Source(94, 90) + SourceIndex(0) -6 >Emitted(45, 87) Source(90, 5) + SourceIndex(0) -7 >Emitted(45, 102) Source(93, 47) + SourceIndex(0) -8 >Emitted(45, 104) Source(90, 5) + SourceIndex(0) -9 >Emitted(45, 171) Source(93, 47) + SourceIndex(0) -10>Emitted(45, 173) Source(91, 9) + SourceIndex(0) -11>Emitted(45, 190) Source(91, 28) + SourceIndex(0) -12>Emitted(45, 192) Source(91, 9) + SourceIndex(0) -13>Emitted(45, 234) Source(91, 28) + SourceIndex(0) -14>Emitted(45, 236) Source(92, 9) + SourceIndex(0) -15>Emitted(45, 255) Source(92, 32) + SourceIndex(0) -16>Emitted(45, 257) Source(92, 9) + SourceIndex(0) -17>Emitted(45, 303) Source(92, 32) + SourceIndex(0) +6 >Emitted(45, 86) Source(90, 5) + SourceIndex(0) +7 >Emitted(45, 101) Source(93, 47) + SourceIndex(0) +8 >Emitted(45, 103) Source(90, 5) + SourceIndex(0) +9 >Emitted(45, 170) Source(93, 47) + SourceIndex(0) +10>Emitted(45, 172) Source(91, 9) + SourceIndex(0) +11>Emitted(45, 189) Source(91, 28) + SourceIndex(0) +12>Emitted(45, 191) Source(91, 9) + SourceIndex(0) +13>Emitted(45, 233) Source(91, 28) + SourceIndex(0) +14>Emitted(45, 235) Source(92, 9) + SourceIndex(0) +15>Emitted(45, 254) Source(92, 32) + SourceIndex(0) +16>Emitted(45, 256) Source(92, 9) + SourceIndex(0) +17>Emitted(45, 302) Source(92, 32) + SourceIndex(0) --- >>> i = 0; i < 1; i++) { 1 >^^^^ @@ -2145,14 +2133,14 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(57, 1) Source(108, 1) + SourceIndex(0) 2 >Emitted(57, 2) Source(108, 2) + SourceIndex(0) --- ->>>for (_22 = multiRobot.name, nameA = _22 === void 0 ? "noName" : _22, _23 = multiRobot.skills, _24 = _23 === void 0 ? { primary: "none", secondary: "none" } : _23, _25 = _24.primary, primaryA = _25 === void 0 ? "primary" : _25, _26 = _24.secondary, secondaryA = _26 === void 0 ? "secondary" : _26, multiRobot, multiRobot, i = 0; i < 1; i++) { +>>>for (_22 = multiRobot.name, nameA = _22 === void 0 ? "noName" : _22, _23 = multiRobot.skills, _24 = _23 === void 0 ? { primary: "none", secondary: "none" } : _23, _25 = _24.primary, primaryA = _25 === void 0 ? "primary" : _25, _26 = _24.secondary, secondaryA = _26 === void 0 ? "secondary" : _26, multiRobot, i = 0; i < 1; i++) { 1-> 2 >^^^ 3 > ^ @@ -2175,20 +2163,18 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 20> ^^ 21> ^^^^^^^^^^ 22> ^^ -23> ^^^^^^^^^^ -24> ^^ -25> ^ -26> ^^^ -27> ^ -28> ^^ -29> ^ -30> ^^^ -31> ^ -32> ^^ -33> ^ -34> ^^ -35> ^^ -36> ^ +23> ^ +24> ^^^ +25> ^ +26> ^^ +27> ^ +28> ^^^ +29> ^ +30> ^^ +31> ^ +32> ^^ +33> ^^ +34> ^ 1-> > 2 >for @@ -2222,21 +2208,19 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = 21> multiRobot -22> -23> multiRobot -24> , -25> i -26> = -27> 0 -28> ; -29> i -30> < -31> 1 -32> ; -33> i -34> ++ -35> ) -36> { +22> , +23> i +24> = +25> 0 +26> ; +27> i +28> < +29> 1 +30> ; +31> i +32> ++ +33> ) +34> { 1->Emitted(58, 1) Source(109, 1) + SourceIndex(0) 2 >Emitted(58, 4) Source(109, 4) + SourceIndex(0) 3 >Emitted(58, 5) Source(109, 5) + SourceIndex(0) @@ -2258,21 +2242,19 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 19>Emitted(58, 296) Source(113, 44) + SourceIndex(0) 20>Emitted(58, 298) Source(115, 5) + SourceIndex(0) 21>Emitted(58, 308) Source(115, 15) + SourceIndex(0) -22>Emitted(58, 310) Source(115, 5) + SourceIndex(0) -23>Emitted(58, 320) Source(115, 15) + SourceIndex(0) -24>Emitted(58, 322) Source(115, 17) + SourceIndex(0) -25>Emitted(58, 323) Source(115, 18) + SourceIndex(0) -26>Emitted(58, 326) Source(115, 21) + SourceIndex(0) -27>Emitted(58, 327) Source(115, 22) + SourceIndex(0) -28>Emitted(58, 329) Source(115, 24) + SourceIndex(0) -29>Emitted(58, 330) Source(115, 25) + SourceIndex(0) -30>Emitted(58, 333) Source(115, 28) + SourceIndex(0) -31>Emitted(58, 334) Source(115, 29) + SourceIndex(0) -32>Emitted(58, 336) Source(115, 31) + SourceIndex(0) -33>Emitted(58, 337) Source(115, 32) + SourceIndex(0) -34>Emitted(58, 339) Source(115, 34) + SourceIndex(0) -35>Emitted(58, 341) Source(115, 36) + SourceIndex(0) -36>Emitted(58, 342) Source(115, 37) + SourceIndex(0) +22>Emitted(58, 310) Source(115, 17) + SourceIndex(0) +23>Emitted(58, 311) Source(115, 18) + SourceIndex(0) +24>Emitted(58, 314) Source(115, 21) + SourceIndex(0) +25>Emitted(58, 315) Source(115, 22) + SourceIndex(0) +26>Emitted(58, 317) Source(115, 24) + SourceIndex(0) +27>Emitted(58, 318) Source(115, 25) + SourceIndex(0) +28>Emitted(58, 321) Source(115, 28) + SourceIndex(0) +29>Emitted(58, 322) Source(115, 29) + SourceIndex(0) +30>Emitted(58, 324) Source(115, 31) + SourceIndex(0) +31>Emitted(58, 325) Source(115, 32) + SourceIndex(0) +32>Emitted(58, 327) Source(115, 34) + SourceIndex(0) +33>Emitted(58, 329) Source(115, 36) + SourceIndex(0) +34>Emitted(58, 330) Source(115, 37) + SourceIndex(0) --- >>> console.log(primaryA); 1 >^^^^ @@ -2304,48 +2286,48 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(60, 1) Source(117, 1) + SourceIndex(0) 2 >Emitted(60, 2) Source(117, 2) + SourceIndex(0) --- ->>>for (_27 = getMultiRobot(), (_28 = _27.name, nameA = _28 === void 0 ? "noName" : _28, _29 = _27.skills, _30 = _29 === void 0 ? { primary: "none", secondary: "none" } : _29, _31 = _30.primary, primaryA = _31 === void 0 ? "primary" : _31, _32 = _30.secondary, secondaryA = _32 === void 0 ? "secondary" : _32, _27), _27, i = 0; i < 1; i++) { +>>>for (_27 = getMultiRobot(), _28 = _27.name, nameA = _28 === void 0 ? "noName" : _28, _29 = _27.skills, _30 = _29 === void 0 ? { primary: "none", secondary: "none" } : _29, _31 = _30.primary, primaryA = _31 === void 0 ? "primary" : _31, _32 = _30.secondary, secondaryA = _32 === void 0 ? "secondary" : _32, _27, i = 0; i < 1; i++) { 1-> 2 >^^^ 3 > ^ 4 > ^ 5 > ^^^^^^^^^^^^^^^^^^^^^ -6 > ^^^ -7 > ^^^^^^^^^^^^^^ -8 > ^^ -9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -10> ^^ -11> ^^^^^^^^^^^^^^^^ -12> ^^ -13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -14> ^^ -15> ^^^^^^^^^^^^^^^^^ -16> ^^ -17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -18> ^^ -19> ^^^^^^^^^^^^^^^^^^^ -20> ^^ -21> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -22> ^^^^^^^^^^^^^ -23> ^ -24> ^^^ -25> ^ -26> ^^ -27> ^ -28> ^^^ -29> ^ -30> ^^ -31> ^ -32> ^^ -33> ^^ -34> ^ +6 > ^^ +7 > ^^^^^^^^^^^^^^ +8 > ^^ +9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10> ^^ +11> ^^^^^^^^^^^^^^^^ +12> ^^ +13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +14> ^^ +15> ^^^^^^^^^^^^^^^^^ +16> ^^ +17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +18> ^^ +19> ^^^^^^^^^^^^^^^^^^^ +20> ^^ +21> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +22> ^^^^^^^ +23> ^ +24> ^^^ +25> ^ +26> ^^ +27> ^ +28> ^^^ +29> ^ +30> ^^ +31> ^ +32> ^^ +33> ^^ +34> ^ 1-> > 2 >for @@ -2359,78 +2341,78 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = getMultiRobot() 6 > -7 > name: nameA = "noName" -8 > -9 > name: nameA = "noName" -10> , - > -11> skills: { - > primary: primaryA = "primary", - > secondary: secondaryA = "secondary" - > } = { primary: "none", secondary: "none" } -12> -13> skills: { - > primary: primaryA = "primary", - > secondary: secondaryA = "secondary" - > } = { primary: "none", secondary: "none" } -14> -15> primary: primaryA = "primary" -16> -17> primary: primaryA = "primary" -18> , - > -19> secondary: secondaryA = "secondary" -20> -21> secondary: secondaryA = "secondary" -22> - > } = { primary: "none", secondary: "none" } - > } = getMultiRobot(), -23> i -24> = -25> 0 -26> ; -27> i -28> < -29> 1 -30> ; -31> i -32> ++ -33> ) -34> { +7 > name: nameA = "noName" +8 > +9 > name: nameA = "noName" +10> , + > +11> skills: { + > primary: primaryA = "primary", + > secondary: secondaryA = "secondary" + > } = { primary: "none", secondary: "none" } +12> +13> skills: { + > primary: primaryA = "primary", + > secondary: secondaryA = "secondary" + > } = { primary: "none", secondary: "none" } +14> +15> primary: primaryA = "primary" +16> +17> primary: primaryA = "primary" +18> , + > +19> secondary: secondaryA = "secondary" +20> +21> secondary: secondaryA = "secondary" +22> + > } = { primary: "none", secondary: "none" } + > } = getMultiRobot(), +23> i +24> = +25> 0 +26> ; +27> i +28> < +29> 1 +30> ; +31> i +32> ++ +33> ) +34> { 1->Emitted(61, 1) Source(118, 1) + SourceIndex(0) 2 >Emitted(61, 4) Source(118, 4) + SourceIndex(0) 3 >Emitted(61, 5) Source(118, 5) + SourceIndex(0) 4 >Emitted(61, 6) Source(118, 6) + SourceIndex(0) 5 >Emitted(61, 27) Source(124, 20) + SourceIndex(0) -6 >Emitted(61, 30) Source(119, 5) + SourceIndex(0) -7 >Emitted(61, 44) Source(119, 27) + SourceIndex(0) -8 >Emitted(61, 46) Source(119, 5) + SourceIndex(0) -9 >Emitted(61, 85) Source(119, 27) + SourceIndex(0) -10>Emitted(61, 87) Source(120, 5) + SourceIndex(0) -11>Emitted(61, 103) Source(123, 47) + SourceIndex(0) -12>Emitted(61, 105) Source(120, 5) + SourceIndex(0) -13>Emitted(61, 172) Source(123, 47) + SourceIndex(0) -14>Emitted(61, 174) Source(121, 9) + SourceIndex(0) -15>Emitted(61, 191) Source(121, 38) + SourceIndex(0) -16>Emitted(61, 193) Source(121, 9) + SourceIndex(0) -17>Emitted(61, 236) Source(121, 38) + SourceIndex(0) -18>Emitted(61, 238) Source(122, 9) + SourceIndex(0) -19>Emitted(61, 257) Source(122, 44) + SourceIndex(0) -20>Emitted(61, 259) Source(122, 9) + SourceIndex(0) -21>Emitted(61, 306) Source(122, 44) + SourceIndex(0) -22>Emitted(61, 319) Source(124, 22) + SourceIndex(0) -23>Emitted(61, 320) Source(124, 23) + SourceIndex(0) -24>Emitted(61, 323) Source(124, 26) + SourceIndex(0) -25>Emitted(61, 324) Source(124, 27) + SourceIndex(0) -26>Emitted(61, 326) Source(124, 29) + SourceIndex(0) -27>Emitted(61, 327) Source(124, 30) + SourceIndex(0) -28>Emitted(61, 330) Source(124, 33) + SourceIndex(0) -29>Emitted(61, 331) Source(124, 34) + SourceIndex(0) -30>Emitted(61, 333) Source(124, 36) + SourceIndex(0) -31>Emitted(61, 334) Source(124, 37) + SourceIndex(0) -32>Emitted(61, 336) Source(124, 39) + SourceIndex(0) -33>Emitted(61, 338) Source(124, 41) + SourceIndex(0) -34>Emitted(61, 339) Source(124, 42) + SourceIndex(0) +6 >Emitted(61, 29) Source(119, 5) + SourceIndex(0) +7 >Emitted(61, 43) Source(119, 27) + SourceIndex(0) +8 >Emitted(61, 45) Source(119, 5) + SourceIndex(0) +9 >Emitted(61, 84) Source(119, 27) + SourceIndex(0) +10>Emitted(61, 86) Source(120, 5) + SourceIndex(0) +11>Emitted(61, 102) Source(123, 47) + SourceIndex(0) +12>Emitted(61, 104) Source(120, 5) + SourceIndex(0) +13>Emitted(61, 171) Source(123, 47) + SourceIndex(0) +14>Emitted(61, 173) Source(121, 9) + SourceIndex(0) +15>Emitted(61, 190) Source(121, 38) + SourceIndex(0) +16>Emitted(61, 192) Source(121, 9) + SourceIndex(0) +17>Emitted(61, 235) Source(121, 38) + SourceIndex(0) +18>Emitted(61, 237) Source(122, 9) + SourceIndex(0) +19>Emitted(61, 256) Source(122, 44) + SourceIndex(0) +20>Emitted(61, 258) Source(122, 9) + SourceIndex(0) +21>Emitted(61, 305) Source(122, 44) + SourceIndex(0) +22>Emitted(61, 312) Source(124, 22) + SourceIndex(0) +23>Emitted(61, 313) Source(124, 23) + SourceIndex(0) +24>Emitted(61, 316) Source(124, 26) + SourceIndex(0) +25>Emitted(61, 317) Source(124, 27) + SourceIndex(0) +26>Emitted(61, 319) Source(124, 29) + SourceIndex(0) +27>Emitted(61, 320) Source(124, 30) + SourceIndex(0) +28>Emitted(61, 323) Source(124, 33) + SourceIndex(0) +29>Emitted(61, 324) Source(124, 34) + SourceIndex(0) +30>Emitted(61, 326) Source(124, 36) + SourceIndex(0) +31>Emitted(61, 327) Source(124, 37) + SourceIndex(0) +32>Emitted(61, 329) Source(124, 39) + SourceIndex(0) +33>Emitted(61, 331) Source(124, 41) + SourceIndex(0) +34>Emitted(61, 332) Source(124, 42) + SourceIndex(0) --- >>> console.log(primaryA); 1 >^^^^ @@ -2462,35 +2444,35 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(63, 1) Source(126, 1) + SourceIndex(0) 2 >Emitted(63, 2) Source(126, 2) + SourceIndex(0) --- ->>>for (_33 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, (_34 = _33.name, nameA = _34 === void 0 ? "noName" : _34, _35 = _33.skills, _36 = _35 === void 0 ? { primary: "none", secondary: "none" } : _35, _37 = _36.primary, primaryA = _37 === void 0 ? "primary" : _37, _38 = _36.secondary, secondaryA = _38 === void 0 ? "secondary" : _38, _33), _33, +>>>for (_33 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, _34 = _33.name, nameA = _34 === void 0 ? "noName" : _34, _35 = _33.skills, _36 = _35 === void 0 ? { primary: "none", secondary: "none" } : _35, _37 = _36.primary, primaryA = _37 === void 0 ? "primary" : _37, _38 = _36.secondary, secondaryA = _38 === void 0 ? "secondary" : _38, _33, 1-> 2 >^^^ 3 > ^ 4 > ^ 5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -6 > ^^^ -7 > ^^^^^^^^^^^^^^ -8 > ^^ -9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -10> ^^ -11> ^^^^^^^^^^^^^^^^ -12> ^^ -13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -14> ^^ -15> ^^^^^^^^^^^^^^^^^ -16> ^^ -17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -18> ^^ -19> ^^^^^^^^^^^^^^^^^^^ -20> ^^ -21> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 > ^^ +7 > ^^^^^^^^^^^^^^ +8 > ^^ +9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10> ^^ +11> ^^^^^^^^^^^^^^^^ +12> ^^ +13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +14> ^^ +15> ^^^^^^^^^^^^^^^^^ +16> ^^ +17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +18> ^^ +19> ^^^^^^^^^^^^^^^^^^^ +20> ^^ +21> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1-> > 2 >for @@ -2504,50 +2486,50 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } } 6 > -7 > name: nameA = "noName" -8 > -9 > name: nameA = "noName" -10> , - > -11> skills: { - > primary: primaryA = "primary", - > secondary: secondaryA = "secondary" - > } = { primary: "none", secondary: "none" } -12> -13> skills: { - > primary: primaryA = "primary", - > secondary: secondaryA = "secondary" - > } = { primary: "none", secondary: "none" } -14> -15> primary: primaryA = "primary" -16> -17> primary: primaryA = "primary" -18> , - > -19> secondary: secondaryA = "secondary" -20> -21> secondary: secondaryA = "secondary" +7 > name: nameA = "noName" +8 > +9 > name: nameA = "noName" +10> , + > +11> skills: { + > primary: primaryA = "primary", + > secondary: secondaryA = "secondary" + > } = { primary: "none", secondary: "none" } +12> +13> skills: { + > primary: primaryA = "primary", + > secondary: secondaryA = "secondary" + > } = { primary: "none", secondary: "none" } +14> +15> primary: primaryA = "primary" +16> +17> primary: primaryA = "primary" +18> , + > +19> secondary: secondaryA = "secondary" +20> +21> secondary: secondaryA = "secondary" 1->Emitted(64, 1) Source(127, 1) + SourceIndex(0) 2 >Emitted(64, 4) Source(127, 4) + SourceIndex(0) 3 >Emitted(64, 5) Source(127, 5) + SourceIndex(0) 4 >Emitted(64, 6) Source(127, 6) + SourceIndex(0) 5 >Emitted(64, 85) Source(133, 90) + SourceIndex(0) -6 >Emitted(64, 88) Source(128, 5) + SourceIndex(0) -7 >Emitted(64, 102) Source(128, 27) + SourceIndex(0) -8 >Emitted(64, 104) Source(128, 5) + SourceIndex(0) -9 >Emitted(64, 143) Source(128, 27) + SourceIndex(0) -10>Emitted(64, 145) Source(129, 5) + SourceIndex(0) -11>Emitted(64, 161) Source(132, 47) + SourceIndex(0) -12>Emitted(64, 163) Source(129, 5) + SourceIndex(0) -13>Emitted(64, 230) Source(132, 47) + SourceIndex(0) -14>Emitted(64, 232) Source(130, 9) + SourceIndex(0) -15>Emitted(64, 249) Source(130, 38) + SourceIndex(0) -16>Emitted(64, 251) Source(130, 9) + SourceIndex(0) -17>Emitted(64, 294) Source(130, 38) + SourceIndex(0) -18>Emitted(64, 296) Source(131, 9) + SourceIndex(0) -19>Emitted(64, 315) Source(131, 44) + SourceIndex(0) -20>Emitted(64, 317) Source(131, 9) + SourceIndex(0) -21>Emitted(64, 364) Source(131, 44) + SourceIndex(0) +6 >Emitted(64, 87) Source(128, 5) + SourceIndex(0) +7 >Emitted(64, 101) Source(128, 27) + SourceIndex(0) +8 >Emitted(64, 103) Source(128, 5) + SourceIndex(0) +9 >Emitted(64, 142) Source(128, 27) + SourceIndex(0) +10>Emitted(64, 144) Source(129, 5) + SourceIndex(0) +11>Emitted(64, 160) Source(132, 47) + SourceIndex(0) +12>Emitted(64, 162) Source(129, 5) + SourceIndex(0) +13>Emitted(64, 229) Source(132, 47) + SourceIndex(0) +14>Emitted(64, 231) Source(130, 9) + SourceIndex(0) +15>Emitted(64, 248) Source(130, 38) + SourceIndex(0) +16>Emitted(64, 250) Source(130, 9) + SourceIndex(0) +17>Emitted(64, 293) Source(130, 38) + SourceIndex(0) +18>Emitted(64, 295) Source(131, 9) + SourceIndex(0) +19>Emitted(64, 314) Source(131, 44) + SourceIndex(0) +20>Emitted(64, 316) Source(131, 9) + SourceIndex(0) +21>Emitted(64, 363) Source(131, 44) + SourceIndex(0) --- >>> i = 0; i < 1; i++) { 1 >^^^^ @@ -2979,14 +2961,14 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(76, 1) Source(146, 1) + SourceIndex(0) 2 >Emitted(76, 2) Source(146, 2) + SourceIndex(0) --- ->>>for (_47 = multiRobot.name, name = _47 === void 0 ? "noName" : _47, _48 = multiRobot.skills, _49 = _48 === void 0 ? { primary: "none", secondary: "none" } : _48, _50 = _49.primary, primary = _50 === void 0 ? "primary" : _50, _51 = _49.secondary, secondary = _51 === void 0 ? "secondary" : _51, multiRobot, multiRobot, i = 0; i < 1; i++) { +>>>for (_47 = multiRobot.name, name = _47 === void 0 ? "noName" : _47, _48 = multiRobot.skills, _49 = _48 === void 0 ? { primary: "none", secondary: "none" } : _48, _50 = _49.primary, primary = _50 === void 0 ? "primary" : _50, _51 = _49.secondary, secondary = _51 === void 0 ? "secondary" : _51, multiRobot, i = 0; i < 1; i++) { 1-> 2 >^^^ 3 > ^ @@ -3009,20 +2991,18 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 20> ^^ 21> ^^^^^^^^^^ 22> ^^ -23> ^^^^^^^^^^ -24> ^^ -25> ^ -26> ^^^ -27> ^ -28> ^^ -29> ^ -30> ^^^ -31> ^ -32> ^^ -33> ^ -34> ^^ -35> ^^ -36> ^ +23> ^ +24> ^^^ +25> ^ +26> ^^ +27> ^ +28> ^^^ +29> ^ +30> ^^ +31> ^ +32> ^^ +33> ^^ +34> ^ 1-> > 2 >for @@ -3056,21 +3036,19 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = 21> multiRobot -22> -23> multiRobot -24> , -25> i -26> = -27> 0 -28> ; -29> i -30> < -31> 1 -32> ; -33> i -34> ++ -35> ) -36> { +22> , +23> i +24> = +25> 0 +26> ; +27> i +28> < +29> 1 +30> ; +31> i +32> ++ +33> ) +34> { 1->Emitted(77, 1) Source(147, 1) + SourceIndex(0) 2 >Emitted(77, 4) Source(147, 4) + SourceIndex(0) 3 >Emitted(77, 5) Source(147, 5) + SourceIndex(0) @@ -3092,21 +3070,19 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 19>Emitted(77, 293) Source(151, 32) + SourceIndex(0) 20>Emitted(77, 295) Source(153, 5) + SourceIndex(0) 21>Emitted(77, 305) Source(153, 15) + SourceIndex(0) -22>Emitted(77, 307) Source(153, 5) + SourceIndex(0) -23>Emitted(77, 317) Source(153, 15) + SourceIndex(0) -24>Emitted(77, 319) Source(153, 17) + SourceIndex(0) -25>Emitted(77, 320) Source(153, 18) + SourceIndex(0) -26>Emitted(77, 323) Source(153, 21) + SourceIndex(0) -27>Emitted(77, 324) Source(153, 22) + SourceIndex(0) -28>Emitted(77, 326) Source(153, 24) + SourceIndex(0) -29>Emitted(77, 327) Source(153, 25) + SourceIndex(0) -30>Emitted(77, 330) Source(153, 28) + SourceIndex(0) -31>Emitted(77, 331) Source(153, 29) + SourceIndex(0) -32>Emitted(77, 333) Source(153, 31) + SourceIndex(0) -33>Emitted(77, 334) Source(153, 32) + SourceIndex(0) -34>Emitted(77, 336) Source(153, 34) + SourceIndex(0) -35>Emitted(77, 338) Source(153, 36) + SourceIndex(0) -36>Emitted(77, 339) Source(153, 37) + SourceIndex(0) +22>Emitted(77, 307) Source(153, 17) + SourceIndex(0) +23>Emitted(77, 308) Source(153, 18) + SourceIndex(0) +24>Emitted(77, 311) Source(153, 21) + SourceIndex(0) +25>Emitted(77, 312) Source(153, 22) + SourceIndex(0) +26>Emitted(77, 314) Source(153, 24) + SourceIndex(0) +27>Emitted(77, 315) Source(153, 25) + SourceIndex(0) +28>Emitted(77, 318) Source(153, 28) + SourceIndex(0) +29>Emitted(77, 319) Source(153, 29) + SourceIndex(0) +30>Emitted(77, 321) Source(153, 31) + SourceIndex(0) +31>Emitted(77, 322) Source(153, 32) + SourceIndex(0) +32>Emitted(77, 324) Source(153, 34) + SourceIndex(0) +33>Emitted(77, 326) Source(153, 36) + SourceIndex(0) +34>Emitted(77, 327) Source(153, 37) + SourceIndex(0) --- >>> console.log(primaryA); 1 >^^^^ @@ -3138,48 +3114,48 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(79, 1) Source(155, 1) + SourceIndex(0) 2 >Emitted(79, 2) Source(155, 2) + SourceIndex(0) --- ->>>for (_52 = getMultiRobot(), (_53 = _52.name, name = _53 === void 0 ? "noName" : _53, _54 = _52.skills, _55 = _54 === void 0 ? { primary: "none", secondary: "none" } : _54, _56 = _55.primary, primary = _56 === void 0 ? "primary" : _56, _57 = _55.secondary, secondary = _57 === void 0 ? "secondary" : _57, _52), _52, i = 0; i < 1; i++) { +>>>for (_52 = getMultiRobot(), _53 = _52.name, name = _53 === void 0 ? "noName" : _53, _54 = _52.skills, _55 = _54 === void 0 ? { primary: "none", secondary: "none" } : _54, _56 = _55.primary, primary = _56 === void 0 ? "primary" : _56, _57 = _55.secondary, secondary = _57 === void 0 ? "secondary" : _57, _52, i = 0; i < 1; i++) { 1-> 2 >^^^ 3 > ^ 4 > ^ 5 > ^^^^^^^^^^^^^^^^^^^^^ -6 > ^^^ -7 > ^^^^^^^^^^^^^^ -8 > ^^ -9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -10> ^^ -11> ^^^^^^^^^^^^^^^^ -12> ^^ -13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -14> ^^ -15> ^^^^^^^^^^^^^^^^^ -16> ^^ -17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -18> ^^ -19> ^^^^^^^^^^^^^^^^^^^ -20> ^^ -21> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -22> ^^^^^^^^^^^^^ -23> ^ -24> ^^^ -25> ^ -26> ^^ -27> ^ -28> ^^^ -29> ^ -30> ^^ -31> ^ -32> ^^ -33> ^^ -34> ^ +6 > ^^ +7 > ^^^^^^^^^^^^^^ +8 > ^^ +9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10> ^^ +11> ^^^^^^^^^^^^^^^^ +12> ^^ +13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +14> ^^ +15> ^^^^^^^^^^^^^^^^^ +16> ^^ +17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +18> ^^ +19> ^^^^^^^^^^^^^^^^^^^ +20> ^^ +21> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +22> ^^^^^^^ +23> ^ +24> ^^^ +25> ^ +26> ^^ +27> ^ +28> ^^^ +29> ^ +30> ^^ +31> ^ +32> ^^ +33> ^^ +34> ^ 1-> > 2 >for @@ -3193,78 +3169,78 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = getMultiRobot() 6 > -7 > name = "noName" -8 > -9 > name = "noName" -10> , - > -11> skills: { - > primary = "primary", - > secondary = "secondary" - > } = { primary: "none", secondary: "none" } -12> -13> skills: { - > primary = "primary", - > secondary = "secondary" - > } = { primary: "none", secondary: "none" } -14> -15> primary = "primary" -16> -17> primary = "primary" -18> , - > -19> secondary = "secondary" -20> -21> secondary = "secondary" -22> - > } = { primary: "none", secondary: "none" } - > } = getMultiRobot(), -23> i -24> = -25> 0 -26> ; -27> i -28> < -29> 1 -30> ; -31> i -32> ++ -33> ) -34> { +7 > name = "noName" +8 > +9 > name = "noName" +10> , + > +11> skills: { + > primary = "primary", + > secondary = "secondary" + > } = { primary: "none", secondary: "none" } +12> +13> skills: { + > primary = "primary", + > secondary = "secondary" + > } = { primary: "none", secondary: "none" } +14> +15> primary = "primary" +16> +17> primary = "primary" +18> , + > +19> secondary = "secondary" +20> +21> secondary = "secondary" +22> + > } = { primary: "none", secondary: "none" } + > } = getMultiRobot(), +23> i +24> = +25> 0 +26> ; +27> i +28> < +29> 1 +30> ; +31> i +32> ++ +33> ) +34> { 1->Emitted(80, 1) Source(156, 1) + SourceIndex(0) 2 >Emitted(80, 4) Source(156, 4) + SourceIndex(0) 3 >Emitted(80, 5) Source(156, 5) + SourceIndex(0) 4 >Emitted(80, 6) Source(156, 6) + SourceIndex(0) 5 >Emitted(80, 27) Source(162, 20) + SourceIndex(0) -6 >Emitted(80, 30) Source(157, 5) + SourceIndex(0) -7 >Emitted(80, 44) Source(157, 20) + SourceIndex(0) -8 >Emitted(80, 46) Source(157, 5) + SourceIndex(0) -9 >Emitted(80, 84) Source(157, 20) + SourceIndex(0) -10>Emitted(80, 86) Source(158, 5) + SourceIndex(0) -11>Emitted(80, 102) Source(161, 47) + SourceIndex(0) -12>Emitted(80, 104) Source(158, 5) + SourceIndex(0) -13>Emitted(80, 171) Source(161, 47) + SourceIndex(0) -14>Emitted(80, 173) Source(159, 9) + SourceIndex(0) -15>Emitted(80, 190) Source(159, 28) + SourceIndex(0) -16>Emitted(80, 192) Source(159, 9) + SourceIndex(0) -17>Emitted(80, 234) Source(159, 28) + SourceIndex(0) -18>Emitted(80, 236) Source(160, 9) + SourceIndex(0) -19>Emitted(80, 255) Source(160, 32) + SourceIndex(0) -20>Emitted(80, 257) Source(160, 9) + SourceIndex(0) -21>Emitted(80, 303) Source(160, 32) + SourceIndex(0) -22>Emitted(80, 316) Source(162, 22) + SourceIndex(0) -23>Emitted(80, 317) Source(162, 23) + SourceIndex(0) -24>Emitted(80, 320) Source(162, 26) + SourceIndex(0) -25>Emitted(80, 321) Source(162, 27) + SourceIndex(0) -26>Emitted(80, 323) Source(162, 29) + SourceIndex(0) -27>Emitted(80, 324) Source(162, 30) + SourceIndex(0) -28>Emitted(80, 327) Source(162, 33) + SourceIndex(0) -29>Emitted(80, 328) Source(162, 34) + SourceIndex(0) -30>Emitted(80, 330) Source(162, 36) + SourceIndex(0) -31>Emitted(80, 331) Source(162, 37) + SourceIndex(0) -32>Emitted(80, 333) Source(162, 39) + SourceIndex(0) -33>Emitted(80, 335) Source(162, 41) + SourceIndex(0) -34>Emitted(80, 336) Source(162, 42) + SourceIndex(0) +6 >Emitted(80, 29) Source(157, 5) + SourceIndex(0) +7 >Emitted(80, 43) Source(157, 20) + SourceIndex(0) +8 >Emitted(80, 45) Source(157, 5) + SourceIndex(0) +9 >Emitted(80, 83) Source(157, 20) + SourceIndex(0) +10>Emitted(80, 85) Source(158, 5) + SourceIndex(0) +11>Emitted(80, 101) Source(161, 47) + SourceIndex(0) +12>Emitted(80, 103) Source(158, 5) + SourceIndex(0) +13>Emitted(80, 170) Source(161, 47) + SourceIndex(0) +14>Emitted(80, 172) Source(159, 9) + SourceIndex(0) +15>Emitted(80, 189) Source(159, 28) + SourceIndex(0) +16>Emitted(80, 191) Source(159, 9) + SourceIndex(0) +17>Emitted(80, 233) Source(159, 28) + SourceIndex(0) +18>Emitted(80, 235) Source(160, 9) + SourceIndex(0) +19>Emitted(80, 254) Source(160, 32) + SourceIndex(0) +20>Emitted(80, 256) Source(160, 9) + SourceIndex(0) +21>Emitted(80, 302) Source(160, 32) + SourceIndex(0) +22>Emitted(80, 309) Source(162, 22) + SourceIndex(0) +23>Emitted(80, 310) Source(162, 23) + SourceIndex(0) +24>Emitted(80, 313) Source(162, 26) + SourceIndex(0) +25>Emitted(80, 314) Source(162, 27) + SourceIndex(0) +26>Emitted(80, 316) Source(162, 29) + SourceIndex(0) +27>Emitted(80, 317) Source(162, 30) + SourceIndex(0) +28>Emitted(80, 320) Source(162, 33) + SourceIndex(0) +29>Emitted(80, 321) Source(162, 34) + SourceIndex(0) +30>Emitted(80, 323) Source(162, 36) + SourceIndex(0) +31>Emitted(80, 324) Source(162, 37) + SourceIndex(0) +32>Emitted(80, 326) Source(162, 39) + SourceIndex(0) +33>Emitted(80, 328) Source(162, 41) + SourceIndex(0) +34>Emitted(80, 329) Source(162, 42) + SourceIndex(0) --- >>> console.log(primaryA); 1 >^^^^ @@ -3296,35 +3272,35 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(82, 1) Source(164, 1) + SourceIndex(0) 2 >Emitted(82, 2) Source(164, 2) + SourceIndex(0) --- ->>>for (_58 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, (_59 = _58.name, name = _59 === void 0 ? "noName" : _59, _60 = _58.skills, _61 = _60 === void 0 ? { primary: "none", secondary: "none" } : _60, _62 = _61.primary, primary = _62 === void 0 ? "primary" : _62, _63 = _61.secondary, secondary = _63 === void 0 ? "secondary" : _63, _58), _58, +>>>for (_58 = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } }, _59 = _58.name, name = _59 === void 0 ? "noName" : _59, _60 = _58.skills, _61 = _60 === void 0 ? { primary: "none", secondary: "none" } : _60, _62 = _61.primary, primary = _62 === void 0 ? "primary" : _62, _63 = _61.secondary, secondary = _63 === void 0 ? "secondary" : _63, _58, 1-> 2 >^^^ 3 > ^ 4 > ^ 5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -6 > ^^^ -7 > ^^^^^^^^^^^^^^ -8 > ^^ -9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -10> ^^ -11> ^^^^^^^^^^^^^^^^ -12> ^^ -13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -14> ^^ -15> ^^^^^^^^^^^^^^^^^ -16> ^^ -17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -18> ^^ -19> ^^^^^^^^^^^^^^^^^^^ -20> ^^ -21> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 > ^^ +7 > ^^^^^^^^^^^^^^ +8 > ^^ +9 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10> ^^ +11> ^^^^^^^^^^^^^^^^ +12> ^^ +13> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +14> ^^ +15> ^^^^^^^^^^^^^^^^^ +16> ^^ +17> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +18> ^^ +19> ^^^^^^^^^^^^^^^^^^^ +20> ^^ +21> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1-> > 2 >for @@ -3338,50 +3314,50 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 > } = { primary: "none", secondary: "none" } > } = { name: "trimmer", skills: { primary: "trimming", secondary: "edging" } } 6 > -7 > name = "noName" -8 > -9 > name = "noName" -10> , - > -11> skills: { - > primary = "primary", - > secondary = "secondary" - > } = { primary: "none", secondary: "none" } -12> -13> skills: { - > primary = "primary", - > secondary = "secondary" - > } = { primary: "none", secondary: "none" } -14> -15> primary = "primary" -16> -17> primary = "primary" -18> , - > -19> secondary = "secondary" -20> -21> secondary = "secondary" +7 > name = "noName" +8 > +9 > name = "noName" +10> , + > +11> skills: { + > primary = "primary", + > secondary = "secondary" + > } = { primary: "none", secondary: "none" } +12> +13> skills: { + > primary = "primary", + > secondary = "secondary" + > } = { primary: "none", secondary: "none" } +14> +15> primary = "primary" +16> +17> primary = "primary" +18> , + > +19> secondary = "secondary" +20> +21> secondary = "secondary" 1->Emitted(83, 1) Source(165, 1) + SourceIndex(0) 2 >Emitted(83, 4) Source(165, 4) + SourceIndex(0) 3 >Emitted(83, 5) Source(165, 5) + SourceIndex(0) 4 >Emitted(83, 6) Source(165, 6) + SourceIndex(0) 5 >Emitted(83, 85) Source(171, 90) + SourceIndex(0) -6 >Emitted(83, 88) Source(166, 5) + SourceIndex(0) -7 >Emitted(83, 102) Source(166, 20) + SourceIndex(0) -8 >Emitted(83, 104) Source(166, 5) + SourceIndex(0) -9 >Emitted(83, 142) Source(166, 20) + SourceIndex(0) -10>Emitted(83, 144) Source(167, 5) + SourceIndex(0) -11>Emitted(83, 160) Source(170, 47) + SourceIndex(0) -12>Emitted(83, 162) Source(167, 5) + SourceIndex(0) -13>Emitted(83, 229) Source(170, 47) + SourceIndex(0) -14>Emitted(83, 231) Source(168, 9) + SourceIndex(0) -15>Emitted(83, 248) Source(168, 28) + SourceIndex(0) -16>Emitted(83, 250) Source(168, 9) + SourceIndex(0) -17>Emitted(83, 292) Source(168, 28) + SourceIndex(0) -18>Emitted(83, 294) Source(169, 9) + SourceIndex(0) -19>Emitted(83, 313) Source(169, 32) + SourceIndex(0) -20>Emitted(83, 315) Source(169, 9) + SourceIndex(0) -21>Emitted(83, 361) Source(169, 32) + SourceIndex(0) +6 >Emitted(83, 87) Source(166, 5) + SourceIndex(0) +7 >Emitted(83, 101) Source(166, 20) + SourceIndex(0) +8 >Emitted(83, 103) Source(166, 5) + SourceIndex(0) +9 >Emitted(83, 141) Source(166, 20) + SourceIndex(0) +10>Emitted(83, 143) Source(167, 5) + SourceIndex(0) +11>Emitted(83, 159) Source(170, 47) + SourceIndex(0) +12>Emitted(83, 161) Source(167, 5) + SourceIndex(0) +13>Emitted(83, 228) Source(170, 47) + SourceIndex(0) +14>Emitted(83, 230) Source(168, 9) + SourceIndex(0) +15>Emitted(83, 247) Source(168, 28) + SourceIndex(0) +16>Emitted(83, 249) Source(168, 9) + SourceIndex(0) +17>Emitted(83, 291) Source(168, 28) + SourceIndex(0) +18>Emitted(83, 293) Source(169, 9) + SourceIndex(0) +19>Emitted(83, 312) Source(169, 32) + SourceIndex(0) +20>Emitted(83, 314) Source(169, 9) + SourceIndex(0) +21>Emitted(83, 360) Source(169, 32) + SourceIndex(0) --- >>> i = 0; i < 1; i++) { 1 >^^^^ @@ -3458,13 +3434,12 @@ sourceFile:sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2 >>>} 1 > 2 >^ -3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +3 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >} 1 >Emitted(86, 1) Source(174, 1) + SourceIndex(0) 2 >Emitted(86, 2) Source(174, 2) + SourceIndex(0) --- ->>>var _k, _q, _4, _9, _27, _33, _52, _58; ->>>var _a, _b, _c, _d, _e, _f, _g, _h, _j, _l, _m, _o, _p, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _5, _6, _7, _8, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _28, _29, _30, _31, _32, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _53, _54, _55, _56, _57, _59, _60, _61, _62, _63; +>>>var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; >>>//# sourceMappingURL=sourceMapValidationDestructuringForObjectBindingPatternDefaultValues2.js.map \ No newline at end of file From e389e08f710665035ac2c1ff2b99ce1e9a0a4d1d Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 15 Nov 2016 14:04:07 -0800 Subject: [PATCH 14/26] Fix argument list for new containing yield --- src/compiler/transformers/generators.ts | 28 +++++++++++++------ .../es5-asyncFunctionNewExpressions.js | 12 ++++---- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 7c9cde59fde..e60d62207e8 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -946,7 +946,7 @@ namespace ts { * @param node The node to visit. */ function visitArrayLiteralExpression(node: ArrayLiteralExpression) { - return visitElements(node.elements, node.multiLine); + return visitElements(node.elements, /*leadingElement*/ undefined, /*location*/ undefined, node.multiLine); } /** @@ -956,7 +956,7 @@ namespace ts { * @param elements The elements to visit. * @param multiLine Whether array literals created should be emitted on multiple lines. */ - function visitElements(elements: NodeArray, _multiLine?: boolean) { + function visitElements(elements: NodeArray, leadingElement?: Expression, location?: TextRange, multiLine?: boolean) { // [source] // ar = [1, yield, 2]; // @@ -971,18 +971,22 @@ namespace ts { const temp = declareLocal(); let hasAssignedTemp = false; if (numInitialElements > 0) { + const initialElements = visitNodes(elements, visitor, isExpression, 0, numInitialElements); emitAssignment(temp, createArrayLiteral( - visitNodes(elements, visitor, isExpression, 0, numInitialElements) + leadingElement + ? [leadingElement, ...initialElements] + : initialElements ) ); + leadingElement = undefined; hasAssignedTemp = true; } const expressions = reduceLeft(elements, reduceElement, [], numInitialElements); return hasAssignedTemp - ? createArrayConcat(temp, [createArrayLiteral(expressions)]) - : createArrayLiteral(expressions); + ? createArrayConcat(temp, [createArrayLiteral(expressions, /*location*/ undefined, multiLine)]) + : createArrayLiteral(leadingElement ? [leadingElement, ...expressions] : expressions, location, multiLine); function reduceElement(expressions: Expression[], element: Expression) { if (containsYield(element) && expressions.length > 0) { @@ -991,11 +995,16 @@ namespace ts { hasAssignedTemp ? createArrayConcat( temp, - [createArrayLiteral(expressions)] + [createArrayLiteral(expressions, /*location*/ undefined, multiLine)] + ) + : createArrayLiteral( + leadingElement ? [leadingElement, ...expressions] : expressions, + /*location*/ undefined, + multiLine ) - : createArrayLiteral(expressions) ); hasAssignedTemp = true; + leadingElement = undefined; expressions = []; } @@ -1131,7 +1140,10 @@ namespace ts { createFunctionApply( cacheExpression(visitNode(target, visitor, isExpression)), thisArg, - visitElements(node.arguments) + visitElements( + node.arguments, + /*leadingElement*/ createVoidZero() + ) ), /*typeArguments*/ undefined, [], diff --git a/tests/baselines/reference/es5-asyncFunctionNewExpressions.js b/tests/baselines/reference/es5-asyncFunctionNewExpressions.js index 486bf0fdd7f..0fb1140f129 100644 --- a/tests/baselines/reference/es5-asyncFunctionNewExpressions.js +++ b/tests/baselines/reference/es5-asyncFunctionNewExpressions.js @@ -119,7 +119,7 @@ function newExpression2() { _a = x.bind; return [4 /*yield*/, y]; case 1: - new (_a.apply(x, [_c.sent(), z]))(); + new (_a.apply(x, [void 0, _c.sent(), z]))(); return [2 /*return*/]; } }); @@ -132,7 +132,7 @@ function newExpression3() { switch (_c.label) { case 0: _a = x.bind; - _b = [y]; + _b = [void 0, y]; return [4 /*yield*/, z]; case 1: new (_a.apply(x, _b.concat([_c.sent()])))(); @@ -280,7 +280,7 @@ function newExpression13() { _b = (_a = x.a).bind; return [4 /*yield*/, y]; case 1: - new (_b.apply(_a, [_d.sent(), z]))(); + new (_b.apply(_a, [void 0, _d.sent(), z]))(); return [2 /*return*/]; } }); @@ -293,7 +293,7 @@ function newExpression14() { switch (_d.label) { case 0: _b = (_a = x.a).bind; - _c = [y]; + _c = [void 0, y]; return [4 /*yield*/, z]; case 1: new (_b.apply(_a, _c.concat([_d.sent()])))(); @@ -362,7 +362,7 @@ function newExpression19() { _b = (_a = x[a]).bind; return [4 /*yield*/, y]; case 1: - new (_b.apply(_a, [_d.sent(), z]))(); + new (_b.apply(_a, [void 0, _d.sent(), z]))(); return [2 /*return*/]; } }); @@ -375,7 +375,7 @@ function newExpression20() { switch (_d.label) { case 0: _b = (_a = x[a]).bind; - _c = [y]; + _c = [void 0, y]; return [4 /*yield*/, z]; case 1: new (_b.apply(_a, _c.concat([_d.sent()])))(); From a0da47fc8906d57b5f39e4be1aa5a6c867ddc021 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 15 Nov 2016 18:09:47 -0800 Subject: [PATCH 15/26] Added missing places for visitParameterList --- src/compiler/transformer.ts | 82 ++++++++---- src/compiler/transformers/es2015.ts | 66 +++++---- src/compiler/transformers/es2017.ts | 17 ++- src/compiler/transformers/esnext.ts | 6 + src/compiler/transformers/generators.ts | 10 +- src/compiler/transformers/ts.ts | 171 ++++++++---------------- src/compiler/types.ts | 3 + src/compiler/visitor.ts | 27 +++- 8 files changed, 192 insertions(+), 190 deletions(-) diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index b005b1906f6..b4a06c72a23 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -154,14 +154,16 @@ namespace ts { * @param transforms An array of Transformers. */ export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]): TransformationResult { - const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; - const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; const enabledSyntaxKindFeatures = new Array(SyntaxKind.Count); + let scopeModificationDisabled = false; + let lexicalEnvironmentStackOffset = 0; - let hoistedVariableDeclarations: VariableDeclaration[]; - let hoistedFunctionDeclarations: FunctionDeclaration[]; - let lexicalEnvironmentDisabled: boolean; + let lexicalEnvironmentVariableDeclarations: VariableDeclaration[]; + let lexicalEnvironmentFunctionDeclarations: FunctionDeclaration[]; + let lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; + let lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; + let lexicalEnvironmentSuspended = false; // The transformation context is provided to each transformer as part of transformer // initialization. @@ -169,10 +171,12 @@ namespace ts { getCompilerOptions: () => host.getCompilerOptions(), getEmitResolver: () => resolver, getEmitHost: () => host, + startLexicalEnvironment, + suspendLexicalEnvironment, + resumeLexicalEnvironment, + endLexicalEnvironment, hoistVariableDeclaration, hoistFunctionDeclaration, - startLexicalEnvironment, - endLexicalEnvironment, onSubstituteNode: (_emitContext, node) => node, enableSubstitution, isSubstitutionEnabled, @@ -188,7 +192,7 @@ namespace ts { const transformed = map(sourceFiles, transformSourceFile); // Disable modification of the lexical environment. - lexicalEnvironmentDisabled = true; + scopeModificationDisabled = true; return { transformed, @@ -283,13 +287,13 @@ namespace ts { * Records a hoisted variable declaration for the provided name within a lexical environment. */ function hoistVariableDeclaration(name: Identifier): void { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase."); + Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); const decl = createVariableDeclaration(name); - if (!hoistedVariableDeclarations) { - hoistedVariableDeclarations = [decl]; + if (!lexicalEnvironmentVariableDeclarations) { + lexicalEnvironmentVariableDeclarations = [decl]; } else { - hoistedVariableDeclarations.push(decl); + lexicalEnvironmentVariableDeclarations.push(decl); } } @@ -297,12 +301,12 @@ namespace ts { * Records a hoisted function declaration within a lexical environment. */ function hoistFunctionDeclaration(func: FunctionDeclaration): void { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase."); - if (!hoistedFunctionDeclarations) { - hoistedFunctionDeclarations = [func]; + Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); + if (!lexicalEnvironmentFunctionDeclarations) { + lexicalEnvironmentFunctionDeclarations = [func]; } else { - hoistedFunctionDeclarations.push(func); + lexicalEnvironmentFunctionDeclarations.push(func); } } @@ -311,17 +315,32 @@ namespace ts { * are pushed onto a stack, and the related storage variables are reset. */ function startLexicalEnvironment(): void { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot start a lexical environment during the print phase."); + Debug.assert(!scopeModificationDisabled, "Cannot start a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended."); // Save the current lexical environment. Rather than resizing the array we adjust the // stack size variable. This allows us to reuse existing array slots we've // already allocated between transformations to avoid allocation and GC overhead during // transformation. - lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedVariableDeclarations; - lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedFunctionDeclarations; + lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentVariableDeclarations; + lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFunctionDeclarations; lexicalEnvironmentStackOffset++; - hoistedVariableDeclarations = undefined; - hoistedFunctionDeclarations = undefined; + lexicalEnvironmentVariableDeclarations = undefined; + lexicalEnvironmentFunctionDeclarations = undefined; + } + + /** Suspends the current lexical environment, usually after visiting a parameter list. */ + function suspendLexicalEnvironment(): void { + Debug.assert(!scopeModificationDisabled, "Cannot suspend a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is already suspended."); + lexicalEnvironmentSuspended = true; + } + + /** Resumes a suspended lexical environment, usually before visiting a function body. */ + function resumeLexicalEnvironment(): void { + Debug.assert(!scopeModificationDisabled, "Cannot resume a lexical environment during the print phase."); + Debug.assert(lexicalEnvironmentSuspended, "Lexical environment is not suspended suspended."); + lexicalEnvironmentSuspended = false; } /** @@ -329,18 +348,19 @@ namespace ts { * any hoisted declarations added in this environment are returned. */ function endLexicalEnvironment(): Statement[] { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot end a lexical environment during the print phase."); + Debug.assert(!scopeModificationDisabled, "Cannot end a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended."); let statements: Statement[]; - if (hoistedVariableDeclarations || hoistedFunctionDeclarations) { - if (hoistedFunctionDeclarations) { - statements = [...hoistedFunctionDeclarations]; + if (lexicalEnvironmentVariableDeclarations || lexicalEnvironmentFunctionDeclarations) { + if (lexicalEnvironmentFunctionDeclarations) { + statements = [...lexicalEnvironmentFunctionDeclarations]; } - if (hoistedVariableDeclarations) { + if (lexicalEnvironmentVariableDeclarations) { const statement = createVariableStatement( /*modifiers*/ undefined, - createVariableDeclarationList(hoistedVariableDeclarations) + createVariableDeclarationList(lexicalEnvironmentVariableDeclarations) ); if (!statements) { @@ -354,8 +374,12 @@ namespace ts { // Restore the previous lexical environment. lexicalEnvironmentStackOffset--; - hoistedVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset]; - hoistedFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset]; + lexicalEnvironmentVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset]; + lexicalEnvironmentFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset]; + if (lexicalEnvironmentStackOffset === 0) { + lexicalEnvironmentVariableDeclarationsStack = []; + lexicalEnvironmentFunctionDeclarationsStack = []; + } return statements; } } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index dc238f27ed0..f95559518dd 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -166,6 +166,7 @@ namespace ts { export function transformES2015(context: TransformationContext) { const { startLexicalEnvironment, + resumeLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, } = context; @@ -822,17 +823,14 @@ namespace ts { * @param hasSynthesizedSuper A value indicating whether the constructor starts with a * synthesized `super` call. */ - function transformConstructorParameters(constructor: ConstructorDeclaration, hasSynthesizedSuper: boolean): ParameterDeclaration[] { + function transformConstructorParameters(constructor: ConstructorDeclaration, 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, 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); - } - - return []; + return visitParameterList(constructor && !hasSynthesizedSuper && constructor.parameters, visitor, context) + || []; } /** @@ -846,7 +844,7 @@ namespace ts { */ function transformConstructorBody(constructor: ConstructorDeclaration | undefined, node: ClassDeclaration | ClassExpression, extendsClauseElement: ExpressionWithTypeArguments, hasSynthesizedSuper: boolean) { const statements: Statement[] = []; - startLexicalEnvironment(); + resumeLexicalEnvironment(); let statementOffset = -1; if (hasSynthesizedSuper) { @@ -1504,8 +1502,17 @@ namespace ts { if (node.transformFlags & TransformFlags.ContainsLexicalThis) { enableSubstitutionsForCapturedThis(); } - - const func = transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined); + const func = createFunctionExpression( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + /*name*/ undefined, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + transformFunctionBody(node), + node + ); + setOriginalNode(func, node); setEmitFlags(func, EmitFlags.CapturesThis); return func; } @@ -1516,7 +1523,17 @@ namespace ts { * @param node a FunctionExpression node. */ function visitFunctionExpression(node: FunctionExpression): Expression { - return transformFunctionLikeToExpression(node, /*location*/ node, node.name); + return updateFunctionExpression( + node, + /*modifiers*/ undefined, + node.name, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + node.transformFlags & TransformFlags.ES2015 + ? transformFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) + ); } /** @@ -1525,19 +1542,18 @@ namespace ts { * @param node a FunctionDeclaration node. */ function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { - return setOriginalNode( - createFunctionDeclaration( - /*decorators*/ undefined, - node.modifiers, - node.asteriskToken, - node.name, - /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), - /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node - ), - /*original*/ node); + return updateFunctionDeclaration( + node, + /*decorators*/ undefined, + node.modifiers, + node.name, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + node.transformFlags & TransformFlags.ES2015 + ? transformFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) + ); } /** @@ -1559,7 +1575,7 @@ namespace ts { node.asteriskToken, name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, saveStateAndInvoke(node, transformFunctionBody), location @@ -1586,7 +1602,7 @@ namespace ts { const body = node.body; let statementOffset: number; - context.startLexicalEnvironment(); + resumeLexicalEnvironment(); if (isBlock(body)) { // ensureUseStrict is false because no new prologue-directive should be added. // addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 0990e9c454f..35de10a5a59 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -14,6 +14,7 @@ namespace ts { const { startLexicalEnvironment, + resumeLexicalEnvironment, endLexicalEnvironment, } = context; @@ -51,18 +52,14 @@ namespace ts { } currentSourceFileExternalHelpersModuleName = node.externalHelpersModuleName; - return visitEachChild(node, visitor, context); } function visitor(node: Node): VisitResult { - if (node.transformFlags & TransformFlags.ContainsES2017) { - return visitorWorker(node); + if ((node.transformFlags & TransformFlags.ContainsES2017) === 0) { + return node; } - return node; - } - function visitorWorker(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.AsyncKeyword: // ES2017 async modifier should be elided for targets < ES2017 @@ -149,7 +146,7 @@ namespace ts { visitNodes(node.modifiers, visitor, isModifier), node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, isAsyncFunctionLike(node) ? transformAsyncFunctionBody(node) @@ -174,7 +171,7 @@ namespace ts { /*modifiers*/ undefined, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, isAsyncFunctionLike(node) ? transformAsyncFunctionBody(node) @@ -192,7 +189,7 @@ namespace ts { node, visitNodes(node.modifiers, visitor, isModifier), /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, isAsyncFunctionLike(node) ? transformAsyncFunctionBody(node) @@ -203,6 +200,8 @@ namespace ts { function transformAsyncFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody; function transformAsyncFunctionBody(node: ArrowFunction): ConciseBody; function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody { + resumeLexicalEnvironment(); + const original = getOriginalNode(node, isFunctionLike); const nodeType = original.type; const promiseConstructor = languageVersion < ScriptTarget.ES2015 ? getPromiseConstructor(nodeType) : undefined; diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 4f16d221a56..933ae88b58a 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -5,11 +5,16 @@ namespace ts { export function transformESNext(context: TransformationContext) { const { + resumeLexicalEnvironment, endLexicalEnvironment } = context; return transformSourceFile; function transformSourceFile(node: SourceFile) { + if (isDeclarationFile(node)) { + return node; + } + return visitEachChild(node, visitor, context); } @@ -343,6 +348,7 @@ namespace ts { function transformFunctionBody(node: FunctionDeclaration | FunctionExpression | ConstructorDeclaration | MethodDeclaration | AccessorDeclaration): FunctionBody; function transformFunctionBody(node: ArrowFunction): ConciseBody; function transformFunctionBody(node: FunctionLikeDeclaration): ConciseBody { + resumeLexicalEnvironment(); let leadingStatements: Statement[]; for (const parameter of node.parameters) { if (parameter.transformFlags & TransformFlags.ContainsObjectRest) { diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 7c9cde59fde..666baf7d123 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -227,7 +227,7 @@ namespace ts { export function transformGenerators(context: TransformationContext) { const { - startLexicalEnvironment, + resumeLexicalEnvironment, endLexicalEnvironment, hoistFunctionDeclaration, hoistVariableDeclaration, @@ -449,11 +449,11 @@ namespace ts { node = setOriginalNode( createFunctionDeclaration( /*decorators*/ undefined, - /*modifiers*/ undefined, + node.modifiers, /*asteriskToken*/ undefined, node.name, /*typeParameters*/ undefined, - node.parameters, + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, transformGeneratorFunctionBody(node.body), /*location*/ node @@ -500,7 +500,7 @@ namespace ts { /*asteriskToken*/ undefined, node.name, /*typeParameters*/ undefined, - node.parameters, + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, transformGeneratorFunctionBody(node.body), /*location*/ node @@ -578,7 +578,7 @@ namespace ts { state = createTempVariable(/*recordTempVariable*/ undefined); // Build the generator - startLexicalEnvironment(); + resumeLexicalEnvironment(); const statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor); diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 1c74b49da5c..a596169ae93 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -21,6 +21,7 @@ namespace ts { export function transformTypeScript(context: TransformationContext) { const { startLexicalEnvironment, + resumeLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, } = context; @@ -885,9 +886,8 @@ namespace ts { // downlevel the '...args' portion less efficiently by naively copying the contents of 'arguments' to an array. // Instead, we'll avoid using a rest parameter and spread into the super call as // 'super(...arguments)' instead of 'super(...args)', as you can see in "transformConstructorBody". - return constructor - ? visitNodes(constructor.parameters, visitor, isParameter) - : []; + return visitParameterList(constructor && constructor.parameters, visitor, context) + || []; } /** @@ -902,8 +902,7 @@ namespace ts { const statements: Statement[] = []; let indexOfFirstStatement = 0; - // The body of a constructor is a new lexical environment - startLexicalEnvironment(); + resumeLexicalEnvironment(); if (constructor) { indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements); @@ -2053,26 +2052,23 @@ namespace ts { if (!shouldEmitFunctionLikeDeclaration(node)) { return undefined; } - - const method = createMethod( + const updated = updateMethod( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), - node.asteriskToken, visitPropertyNameOfClassElement(node), /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setCommentRange(method, node); - setSourceMapRange(method, moveRangePastDecorators(node)); - setOriginalNode(method, node); - - return method; + if (updated !== node) { + // While we emit the source map for the node after skipping decorators and modifiers, + // we need to emit the comments for the original range. + setCommentRange(updated, node); + setSourceMapRange(updated, moveRangePastDecorators(node)); + } + return updated; } /** @@ -2098,24 +2094,22 @@ namespace ts { if (!shouldEmitAccessorDeclaration(node)) { return undefined; } - - const accessor = createGetAccessor( + const updated = updateGetAccessor( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), visitPropertyNameOfClassElement(node), - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - node.body ? visitEachChild(node.body, visitor, context) : createBlock([]), - /*location*/ node + visitFunctionBody(node.body, visitor, context) || createBlock([]) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setOriginalNode(accessor, node); - setCommentRange(accessor, node); - setSourceMapRange(accessor, moveRangePastDecorators(node)); - - return accessor; + if (updated !== node) { + // While we emit the source map for the node after skipping decorators and modifiers, + // we need to emit the comments for the original range. + setCommentRange(updated, node); + setSourceMapRange(updated, moveRangePastDecorators(node)); + } + return updated; } /** @@ -2131,23 +2125,21 @@ namespace ts { if (!shouldEmitAccessorDeclaration(node)) { return undefined; } - - const accessor = createSetAccessor( + const updated = updateSetAccessor( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), visitPropertyNameOfClassElement(node), - visitNodes(node.parameters, visitor, isParameter), - node.body ? visitEachChild(node.body, visitor, context) : createBlock([]), - /*location*/ node + visitParameterList(node.parameters, visitor, context), + visitFunctionBody(node.body, visitor, context) || createBlock([]) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setOriginalNode(accessor, node); - setCommentRange(accessor, node); - setSourceMapRange(accessor, moveRangePastDecorators(node)); - - return accessor; + if (updated !== node) { + // While we emit the source map for the node after skipping decorators and modifiers, + // we need to emit the comments for the original range. + setCommentRange(updated, node); + setSourceMapRange(updated, moveRangePastDecorators(node)); + } + return updated; } /** @@ -2164,27 +2156,22 @@ namespace ts { if (!shouldEmitFunctionLikeDeclaration(node)) { return createNotEmittedStatement(node); } - - const func = createFunctionDeclaration( + const updated = updateFunctionDeclaration( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) || createBlock([]) ); - setOriginalNode(func, node); - if (isNamespaceExport(node)) { - const statements: Statement[] = [func]; + const statements: Statement[] = [updated]; addExportMemberAssignment(statements, node); return statements; } - - return func; + return updated; } /** @@ -2199,21 +2186,16 @@ namespace ts { if (nodeIsMissing(node.body)) { return createOmittedExpression(); } - - const func = createFunctionExpression( + const updated = updateFunctionExpression( + node, visitNodes(node.modifiers, modifierVisitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) ); - - setOriginalNode(func, node); - - return func; + return updated; } /** @@ -2222,62 +2204,15 @@ namespace ts { * - The node has type annotations */ function visitArrowFunction(node: ArrowFunction) { - const func = createArrowFunction( + const updated = updateArrowFunction( + node, visitNodes(node.modifiers, modifierVisitor, isModifier), /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - node.equalsGreaterThanToken, - transformConciseBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) ); - - setOriginalNode(func, node); - - return func; - } - - function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody { - return transformFunctionBodyWorker(node.body); - } - - function transformFunctionBodyWorker(body: Block, start = 0) { - const savedCurrentScope = currentScope; - const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName; - currentScope = body; - currentScopeFirstDeclarationsOfName = createMap(); - startLexicalEnvironment(); - - const statements = visitNodes(body.statements, visitor, isStatement, start); - const visited = updateBlock(body, statements); - const declarations = endLexicalEnvironment(); - currentScope = savedCurrentScope; - currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName; - return mergeFunctionBodyLexicalEnvironment(visited, declarations); - } - - function transformConciseBody(node: ArrowFunction): ConciseBody { - return transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ false); - } - - function transformConciseBodyWorker(body: Block | Expression, forceBlockFunctionBody: boolean) { - if (isBlock(body)) { - return transformFunctionBodyWorker(body); - } - else { - startLexicalEnvironment(); - const visited: Expression | Block = visitNode(body, visitor, isConciseBody); - const declarations = endLexicalEnvironment(); - const merged = mergeFunctionBodyLexicalEnvironment(visited, declarations); - if (forceBlockFunctionBody && !isBlock(merged)) { - return createBlock([ - createReturn(merged) - ]); - } - else { - return merged; - } - } + return updated; } /** diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f99a644137a..deb6f350c14 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3681,6 +3681,9 @@ namespace ts { /** Starts a new lexical environment. */ startLexicalEnvironment(): void; + suspendLexicalEnvironment(): void; + resumeLexicalEnvironment(): void; + /** Ends a lexical environment, returning any declarations. */ endLexicalEnvironment(): Statement[]; } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 825e0b45e45..e75fdcb488b 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -665,21 +665,40 @@ namespace ts { return updated || nodes; } - export function visitLexicalEnvironment(nodes: NodeArray, visitor: (node: Node) => VisitResult, context: LexicalEnvironment, start?: number) { + /** + * Starts a new lexical environment and visits a statement list, ending the lexical environment + * and merging hoisted declarations upon completion. + */ + export function visitLexicalEnvironment(statements: NodeArray, visitor: (node: Node) => VisitResult, context: LexicalEnvironment, start?: number) { context.startLexicalEnvironment(); - const updated = visitNodes(nodes, visitor, isStatement, start); + statements = visitNodes(statements, visitor, isStatement, start); const declarations = context.endLexicalEnvironment(); - return createNodeArray(concatenate(updated, declarations), updated); + return createNodeArray(concatenate(statements, declarations), statements); } + /** + * Starts a new lexical environment and visits a parameter list, suspending the lexical + * environment upon completion. + */ export function visitParameterList(nodes: NodeArray, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { context.startLexicalEnvironment(); - return visitNodes(nodes, visitor, isParameter); + const updated = visitNodes(nodes, visitor, isParameter); + context.suspendLexicalEnvironment(); + return updated; } + /** + * Resumes a suspended lexical environment and visits a function body, ending the lexical + * environment and merging hoisted declarations upon completion. + */ export function visitFunctionBody(node: FunctionBody, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): FunctionBody; + /** + * Resumes a suspended lexical environment and visits a concise body, ending the lexical + * environment and merging hoisted declarations upon completion. + */ export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): ConciseBody; export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { + context.resumeLexicalEnvironment(); const visited = visitNode(node, visitor, isConciseBody); const declarations = context.endLexicalEnvironment(); if (some(declarations)) { From c5ab4735ac01d316e4384e5ea398bd8d271c2ff0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 16 Nov 2016 06:48:51 -0800 Subject: [PATCH 16/26] Resolve upfront so recursive mapped types appear empty --- src/compiler/checker.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 44bbfbc9ad5..846bb288d6b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4489,6 +4489,8 @@ namespace ts { const members: SymbolTable = createMap(); let stringIndexInfo: IndexInfo; let numberIndexInfo: IndexInfo; + // Resolve upfront such that recursive references see an empty object type. + setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined); // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type, // and T as the template type. const typeParameter = getTypeParameterFromMappedType(type); From c00a04d781dae1dff928174427e926f5b144c706 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 16 Nov 2016 06:49:04 -0800 Subject: [PATCH 17/26] Add tests --- .../types/mapped/recursiveMappedTypes.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/cases/conformance/types/mapped/recursiveMappedTypes.ts diff --git a/tests/cases/conformance/types/mapped/recursiveMappedTypes.ts b/tests/cases/conformance/types/mapped/recursiveMappedTypes.ts new file mode 100644 index 00000000000..7a78ad9dc4a --- /dev/null +++ b/tests/cases/conformance/types/mapped/recursiveMappedTypes.ts @@ -0,0 +1,15 @@ +// @declaration: true + +// Recursive mapped types simply appear empty + +type Recurse = { + [K in keyof Recurse]: Recurse[K] +} + +type Recurse1 = { + [K in keyof Recurse2]: Recurse2[K] +} + +type Recurse2 = { + [K in keyof Recurse1]: Recurse1[K] +} \ No newline at end of file From 78726aeb3d46b4535a2f2a49b321c6ad1f413b1a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 16 Nov 2016 06:49:15 -0800 Subject: [PATCH 18/26] Accept new baselines --- .../reference/recursiveMappedTypes.js | 30 +++++++++++++++++ .../reference/recursiveMappedTypes.symbols | 33 +++++++++++++++++++ .../reference/recursiveMappedTypes.types | 33 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 tests/baselines/reference/recursiveMappedTypes.js create mode 100644 tests/baselines/reference/recursiveMappedTypes.symbols create mode 100644 tests/baselines/reference/recursiveMappedTypes.types diff --git a/tests/baselines/reference/recursiveMappedTypes.js b/tests/baselines/reference/recursiveMappedTypes.js new file mode 100644 index 00000000000..dfe69457ed7 --- /dev/null +++ b/tests/baselines/reference/recursiveMappedTypes.js @@ -0,0 +1,30 @@ +//// [recursiveMappedTypes.ts] + +// Recursive mapped types simply appear empty + +type Recurse = { + [K in keyof Recurse]: Recurse[K] +} + +type Recurse1 = { + [K in keyof Recurse2]: Recurse2[K] +} + +type Recurse2 = { + [K in keyof Recurse1]: Recurse1[K] +} + +//// [recursiveMappedTypes.js] +// Recursive mapped types simply appear empty + + +//// [recursiveMappedTypes.d.ts] +declare type Recurse = { + [K in keyof Recurse]: Recurse[K]; +}; +declare type Recurse1 = { + [K in keyof Recurse2]: Recurse2[K]; +}; +declare type Recurse2 = { + [K in keyof Recurse1]: Recurse1[K]; +}; diff --git a/tests/baselines/reference/recursiveMappedTypes.symbols b/tests/baselines/reference/recursiveMappedTypes.symbols new file mode 100644 index 00000000000..ae4ca8d9cc9 --- /dev/null +++ b/tests/baselines/reference/recursiveMappedTypes.symbols @@ -0,0 +1,33 @@ +=== tests/cases/conformance/types/mapped/recursiveMappedTypes.ts === + +// Recursive mapped types simply appear empty + +type Recurse = { +>Recurse : Symbol(Recurse, Decl(recursiveMappedTypes.ts, 0, 0)) + + [K in keyof Recurse]: Recurse[K] +>K : Symbol(K, Decl(recursiveMappedTypes.ts, 4, 5)) +>Recurse : Symbol(Recurse, Decl(recursiveMappedTypes.ts, 0, 0)) +>Recurse : Symbol(Recurse, Decl(recursiveMappedTypes.ts, 0, 0)) +>K : Symbol(K, Decl(recursiveMappedTypes.ts, 4, 5)) +} + +type Recurse1 = { +>Recurse1 : Symbol(Recurse1, Decl(recursiveMappedTypes.ts, 5, 1)) + + [K in keyof Recurse2]: Recurse2[K] +>K : Symbol(K, Decl(recursiveMappedTypes.ts, 8, 5)) +>Recurse2 : Symbol(Recurse2, Decl(recursiveMappedTypes.ts, 9, 1)) +>Recurse2 : Symbol(Recurse2, Decl(recursiveMappedTypes.ts, 9, 1)) +>K : Symbol(K, Decl(recursiveMappedTypes.ts, 8, 5)) +} + +type Recurse2 = { +>Recurse2 : Symbol(Recurse2, Decl(recursiveMappedTypes.ts, 9, 1)) + + [K in keyof Recurse1]: Recurse1[K] +>K : Symbol(K, Decl(recursiveMappedTypes.ts, 12, 5)) +>Recurse1 : Symbol(Recurse1, Decl(recursiveMappedTypes.ts, 5, 1)) +>Recurse1 : Symbol(Recurse1, Decl(recursiveMappedTypes.ts, 5, 1)) +>K : Symbol(K, Decl(recursiveMappedTypes.ts, 12, 5)) +} diff --git a/tests/baselines/reference/recursiveMappedTypes.types b/tests/baselines/reference/recursiveMappedTypes.types new file mode 100644 index 00000000000..2c60bc8b68a --- /dev/null +++ b/tests/baselines/reference/recursiveMappedTypes.types @@ -0,0 +1,33 @@ +=== tests/cases/conformance/types/mapped/recursiveMappedTypes.ts === + +// Recursive mapped types simply appear empty + +type Recurse = { +>Recurse : Recurse + + [K in keyof Recurse]: Recurse[K] +>K : K +>Recurse : Recurse +>Recurse : Recurse +>K : K +} + +type Recurse1 = { +>Recurse1 : Recurse1 + + [K in keyof Recurse2]: Recurse2[K] +>K : K +>Recurse2 : Recurse2 +>Recurse2 : Recurse2 +>K : K +} + +type Recurse2 = { +>Recurse2 : Recurse2 + + [K in keyof Recurse1]: Recurse1[K] +>K : K +>Recurse1 : Recurse1 +>Recurse1 : Recurse1 +>K : K +} From cd023b271554983276f432c2d8830302617255dc Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 16 Nov 2016 10:06:16 -0800 Subject: [PATCH 19/26] PR Feedback --- src/compiler/transformer.ts | 16 ++++----- src/compiler/transformers/destructuring.ts | 39 +++++----------------- src/compiler/transformers/es2015.ts | 4 +-- 3 files changed, 18 insertions(+), 41 deletions(-) diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index b4a06c72a23..fbf8eb9e762 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -156,7 +156,7 @@ namespace ts { export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]): TransformationResult { const enabledSyntaxKindFeatures = new Array(SyntaxKind.Count); - let scopeModificationDisabled = false; + let lexicalEnvironmentDisabled = false; let lexicalEnvironmentStackOffset = 0; let lexicalEnvironmentVariableDeclarations: VariableDeclaration[]; @@ -192,7 +192,7 @@ namespace ts { const transformed = map(sourceFiles, transformSourceFile); // Disable modification of the lexical environment. - scopeModificationDisabled = true; + lexicalEnvironmentDisabled = true; return { transformed, @@ -287,7 +287,7 @@ namespace ts { * Records a hoisted variable declaration for the provided name within a lexical environment. */ function hoistVariableDeclaration(name: Identifier): void { - Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase."); const decl = createVariableDeclaration(name); if (!lexicalEnvironmentVariableDeclarations) { lexicalEnvironmentVariableDeclarations = [decl]; @@ -301,7 +301,7 @@ namespace ts { * Records a hoisted function declaration within a lexical environment. */ function hoistFunctionDeclaration(func: FunctionDeclaration): void { - Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase."); if (!lexicalEnvironmentFunctionDeclarations) { lexicalEnvironmentFunctionDeclarations = [func]; } @@ -315,7 +315,7 @@ namespace ts { * are pushed onto a stack, and the related storage variables are reset. */ function startLexicalEnvironment(): void { - Debug.assert(!scopeModificationDisabled, "Cannot start a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentDisabled, "Cannot start a lexical environment during the print phase."); Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended."); // Save the current lexical environment. Rather than resizing the array we adjust the @@ -331,14 +331,14 @@ namespace ts { /** Suspends the current lexical environment, usually after visiting a parameter list. */ function suspendLexicalEnvironment(): void { - Debug.assert(!scopeModificationDisabled, "Cannot suspend a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentDisabled, "Cannot suspend a lexical environment during the print phase."); Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is already suspended."); lexicalEnvironmentSuspended = true; } /** Resumes a suspended lexical environment, usually before visiting a function body. */ function resumeLexicalEnvironment(): void { - Debug.assert(!scopeModificationDisabled, "Cannot resume a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentDisabled, "Cannot resume a lexical environment during the print phase."); Debug.assert(lexicalEnvironmentSuspended, "Lexical environment is not suspended suspended."); lexicalEnvironmentSuspended = false; } @@ -348,7 +348,7 @@ namespace ts { * any hoisted declarations added in this environment are returned. */ function endLexicalEnvironment(): Statement[] { - Debug.assert(!scopeModificationDisabled, "Cannot end a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentDisabled, "Cannot end a lexical environment during the print phase."); Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended."); let statements: Statement[]; diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 6159cd55a4c..3e63542ab7e 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -6,7 +6,7 @@ namespace ts { interface FlattenContext { context: TransformationContext; level: FlattenLevel; - doNotRecordTempVariablesInLine: boolean; + hoistTempVariables: boolean; emitExpression: (value: Expression) => void; emitBindingOrAssignment: (target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node) => void; createArrayBindingOrAssignmentPattern: (elements: BindingOrAssignmentElement[]) => ArrayBindingOrAssignmentPattern; @@ -57,7 +57,7 @@ namespace ts { const flattenContext: FlattenContext = { context, level, - doNotRecordTempVariablesInLine: true, + hoistTempVariables: true, emitExpression, emitBindingOrAssignment, createArrayBindingOrAssignmentPattern: makeArrayAssignmentPattern, @@ -126,7 +126,7 @@ namespace ts { * @param context The transformation context. * @param boundValue The value bound to the declaration. * @param skipInitializer A value indicating whether to ignore the initializer of `node`. - * @param doNotRecordTempVariablesInLine Indicates whether temporary variables should not be recored in-line. + * @param hoistTempVariables Indicates whether temporary variables should not be recorded in-line. * @param level Indicates the extent to which flattening should occur. */ export function flattenDestructuringBinding( @@ -135,16 +135,15 @@ namespace ts { context: TransformationContext, level: FlattenLevel, rval?: Expression, - doNotRecordTempVariablesInLine?: boolean, + hoistTempVariables?: boolean, skipInitializer?: boolean): VariableDeclaration[] { - let pendingExpressions: Expression[]; const pendingDeclarations: { pendingExpressions?: Expression[], name: BindingName, value: Expression, location?: TextRange, original?: Node; }[] = []; const declarations: VariableDeclaration[] = []; const flattenContext: FlattenContext = { context, level, - doNotRecordTempVariablesInLine, + hoistTempVariables, emitExpression, emitBindingOrAssignment, createArrayBindingOrAssignmentPattern: makeArrayBindingPattern, @@ -152,12 +151,10 @@ namespace ts { createArrayBindingOrAssignmentElement: makeBindingElement, visitor }; - flattenBindingOrAssignmentElement(flattenContext, node, rval, node, skipInitializer); - if (pendingExpressions) { const temp = createTempVariable(/*recordTempVariable*/ undefined); - if (doNotRecordTempVariablesInLine) { + if (hoistTempVariables) { const value = inlineExpressions(pendingExpressions); pendingExpressions = undefined; emitBindingOrAssignment(temp, value, /*location*/ undefined, /*original*/ undefined); @@ -173,7 +170,6 @@ namespace ts { pendingDeclaration.value = temp; } } - for (const { pendingExpressions, name, value, location, original } of pendingDeclarations) { const variable = createVariableDeclaration( name, @@ -187,7 +183,6 @@ namespace ts { aggregateTransformFlags(variable); declarations.push(variable); } - return declarations; function emitExpression(value: Expression) { @@ -330,7 +325,7 @@ namespace ts { // can perform the ObjectRest destructuring in a different declaration if (element.transformFlags & TransformFlags.ContainsObjectRest) { const temp = createTempVariable(/*recordTempVariable*/ undefined); - if (flattenContext.doNotRecordTempVariablesInLine) { + if (flattenContext.hoistTempVariables) { flattenContext.context.hoistVariableDeclaration(temp); } @@ -419,7 +414,7 @@ namespace ts { } else { const temp = createTempVariable(/*recordTempVariable*/ undefined); - if (flattenContext.doNotRecordTempVariablesInLine) { + if (flattenContext.hoistTempVariables) { flattenContext.context.hoistVariableDeclaration(temp); flattenContext.emitExpression(createAssignment(temp, value, location)); } @@ -430,46 +425,28 @@ namespace ts { } } - /** - * Creates an ArrayBindingPattern from an array of BindingOrAssignmentElement nodes. - */ function makeArrayBindingPattern(elements: BindingOrAssignmentElement[]) { Debug.assertEachNode(elements, isArrayBindingElement); return createArrayBindingPattern(elements); } - /** - * Creates an ArrayLiteralExpression assignment pattern from an array of BindingOrAssignmentElement nodes. - */ function makeArrayAssignmentPattern(elements: BindingOrAssignmentElement[]) { return createArrayLiteral(map(elements, convertToArrayAssignmentElement)); } - /** - * Creates an ObjectBindingPattern from an array of BindingOrAssignmentElement nodes. - */ function makeObjectBindingPattern(elements: BindingOrAssignmentElement[]) { Debug.assertEachNode(elements, isBindingElement); return createObjectBindingPattern(elements); } - /** - * Creates an ObjectLiteralExpression assignment pattern from an array of BindingOrAssignmentElement nodes. - */ function makeObjectAssignmentPattern(elements: BindingOrAssignmentElement[]) { return createObjectLiteral(map(elements, convertToObjectAssignmentElement)); } - /** - * Creates a BindingElement for a name. - */ function makeBindingElement(name: Identifier) { return createBindingElement(/*propertyName*/ undefined, /*dotDotDotToken*/ undefined, name); } - /** - * Creates an assignment element for a name. - */ function makeAssignmentElement(name: Identifier) { return name; } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index f95559518dd..ca83b40dc68 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -1896,7 +1896,7 @@ namespace ts { function visitVariableDeclaration(node: VariableDeclaration): VisitResult { // If we are here it is because the name contains a binding pattern. if (isBindingPattern(node.name)) { - const doNotRecordTempVariablesInLine = enclosingVariableStatement + const hoistTempVariables = enclosingVariableStatement && hasModifier(enclosingVariableStatement, ModifierFlags.Export); return flattenDestructuringBinding( node, @@ -1904,7 +1904,7 @@ namespace ts { context, FlattenLevel.All, /*value*/ undefined, - doNotRecordTempVariablesInLine + hoistTempVariables ); } From 8d24712d8563b4d260da250db5a78dba3414f490 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 16 Nov 2016 10:14:16 -0800 Subject: [PATCH 20/26] Improve __assign emit:remove getOwnPropertySymbols Since __assign defaults to Object.assign if present, our own helper will never be used in ES6 and higher, and `getOwnPropertySymbols` is not needed in ES5 and below because symbols don't exist there. --- src/compiler/emitter.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index eacfd1f0b67..3441644f92b 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -38,9 +38,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; };`; From e7bfd02ca9f5ee39cd44b29d9ce450e99b311363 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 16 Nov 2016 10:15:57 -0800 Subject: [PATCH 21/26] Update baselines with new __assign emit --- tests/baselines/reference/importHelpersInTsx.js | 3 --- tests/baselines/reference/objectRest2.js | 3 --- tests/baselines/reference/objectRestForOf.js | 3 --- tests/baselines/reference/objectSpread.js | 3 --- tests/baselines/reference/objectSpreadComputedProperty.js | 3 --- tests/baselines/reference/objectSpreadIndexSignature.js | 3 --- tests/baselines/reference/objectSpreadNegative.js | 3 --- tests/baselines/reference/objectSpreadNegativeParse.js | 3 --- tests/baselines/reference/objectSpreadStrictNull.js | 3 --- tests/baselines/reference/reactNamespaceJSXEmit.js | 3 --- tests/baselines/reference/tsxExternalModuleEmit2.js | 3 --- tests/baselines/reference/tsxReactEmit2.js | 3 --- tests/baselines/reference/tsxReactEmit4.js | 3 --- tests/baselines/reference/tsxReactEmit5.js | 3 --- tests/baselines/reference/tsxReactEmit6.js | 3 --- 15 files changed, 45 deletions(-) diff --git a/tests/baselines/reference/importHelpersInTsx.js b/tests/baselines/reference/importHelpersInTsx.js index d1fe0df2c51..29e43e191e8 100644 --- a/tests/baselines/reference/importHelpersInTsx.js +++ b/tests/baselines/reference/importHelpersInTsx.js @@ -29,9 +29,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/objectRest2.js b/tests/baselines/reference/objectRest2.js index 5e3f17b857f..e21c8c79853 100644 --- a/tests/baselines/reference/objectRest2.js +++ b/tests/baselines/reference/objectRest2.js @@ -20,9 +20,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/objectRestForOf.js b/tests/baselines/reference/objectRestForOf.js index 0fa589737bf..370e0a706a8 100644 --- a/tests/baselines/reference/objectRestForOf.js +++ b/tests/baselines/reference/objectRestForOf.js @@ -20,9 +20,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/objectSpread.js b/tests/baselines/reference/objectSpread.js index dc49d1f40ff..4305e17ab31 100644 --- a/tests/baselines/reference/objectSpread.js +++ b/tests/baselines/reference/objectSpread.js @@ -87,9 +87,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/objectSpreadComputedProperty.js b/tests/baselines/reference/objectSpreadComputedProperty.js index 6f27e696ae7..90a22ce6ff5 100644 --- a/tests/baselines/reference/objectSpreadComputedProperty.js +++ b/tests/baselines/reference/objectSpreadComputedProperty.js @@ -16,9 +16,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/objectSpreadIndexSignature.js b/tests/baselines/reference/objectSpreadIndexSignature.js index ffe88a89c9a..22e92e6a844 100644 --- a/tests/baselines/reference/objectSpreadIndexSignature.js +++ b/tests/baselines/reference/objectSpreadIndexSignature.js @@ -23,9 +23,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/objectSpreadNegative.js b/tests/baselines/reference/objectSpreadNegative.js index 472a0857169..6287f4559a7 100644 --- a/tests/baselines/reference/objectSpreadNegative.js +++ b/tests/baselines/reference/objectSpreadNegative.js @@ -77,9 +77,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/objectSpreadNegativeParse.js b/tests/baselines/reference/objectSpreadNegativeParse.js index 4076ab1c3a0..297c56c3e62 100644 --- a/tests/baselines/reference/objectSpreadNegativeParse.js +++ b/tests/baselines/reference/objectSpreadNegativeParse.js @@ -11,9 +11,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/objectSpreadStrictNull.js b/tests/baselines/reference/objectSpreadStrictNull.js index fc0d4a80564..84604d728cd 100644 --- a/tests/baselines/reference/objectSpreadStrictNull.js +++ b/tests/baselines/reference/objectSpreadStrictNull.js @@ -27,9 +27,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/reactNamespaceJSXEmit.js b/tests/baselines/reference/reactNamespaceJSXEmit.js index 48e514c2538..3a21504bb80 100644 --- a/tests/baselines/reference/reactNamespaceJSXEmit.js +++ b/tests/baselines/reference/reactNamespaceJSXEmit.js @@ -18,9 +18,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/tsxExternalModuleEmit2.js b/tests/baselines/reference/tsxExternalModuleEmit2.js index d3a6591cdaa..6c01a48af78 100644 --- a/tests/baselines/reference/tsxExternalModuleEmit2.js +++ b/tests/baselines/reference/tsxExternalModuleEmit2.js @@ -24,9 +24,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/tsxReactEmit2.js b/tests/baselines/reference/tsxReactEmit2.js index 4ddf442b53f..80e3215e2b6 100644 --- a/tests/baselines/reference/tsxReactEmit2.js +++ b/tests/baselines/reference/tsxReactEmit2.js @@ -21,9 +21,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/tsxReactEmit4.js b/tests/baselines/reference/tsxReactEmit4.js index 8ccc940880f..33c835d1ab2 100644 --- a/tests/baselines/reference/tsxReactEmit4.js +++ b/tests/baselines/reference/tsxReactEmit4.js @@ -23,9 +23,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/tsxReactEmit5.js b/tests/baselines/reference/tsxReactEmit5.js index 6e4d43dd676..c3e58d0a0da 100644 --- a/tests/baselines/reference/tsxReactEmit5.js +++ b/tests/baselines/reference/tsxReactEmit5.js @@ -28,9 +28,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; diff --git a/tests/baselines/reference/tsxReactEmit6.js b/tests/baselines/reference/tsxReactEmit6.js index 4c951354aa8..85aa8c123c9 100644 --- a/tests/baselines/reference/tsxReactEmit6.js +++ b/tests/baselines/reference/tsxReactEmit6.js @@ -33,9 +33,6 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - if (typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) - t[p[i]] = s[p[i]]; } return t; }; From 4e954a1962a75d25efa345cfe42b41256dd47ee4 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 16 Nov 2016 13:25:48 -0800 Subject: [PATCH 22/26] Update issue template --- issue_template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/issue_template.md b/issue_template.md index fcd995317f5..4d397a0afd6 100644 --- a/issue_template.md +++ b/issue_template.md @@ -2,7 +2,7 @@ -**TypeScript Version:** 2.0.3 / nightly (2.1.0-dev.201xxxxx) +**TypeScript Version:** 2.1.1 / nightly (2.2.0-dev.201xxxxx) **Code** @@ -13,4 +13,4 @@ **Expected behavior:** -**Actual behavior:** +**Actual behavior:** From 6f409b580e7ee65c90b1a825e98a21acb59c0909 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Wed, 16 Nov 2016 14:13:29 -0800 Subject: [PATCH 23/26] Fix grammar issue (#12096) * Fix grammar issue * Improve the error message * cr feedback --- src/compiler/commandLineParser.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- src/harness/unittests/configurationExtension.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index df9c3c4abdd..26877de43c4 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -884,7 +884,7 @@ namespace ts { function tryExtendsName(extendedConfig: string): [string[], string[], string[], CompilerOptions] { // If the path isn't a rooted or relative path, don't try to resolve it (we reserve the right to special case module-id like paths in the future) if (!(isRootedDiskPath(extendedConfig) || startsWith(normalizeSlashes(extendedConfig), "./") || startsWith(normalizeSlashes(extendedConfig), "../"))) { - errors.push(createCompilerDiagnostic(Diagnostics.The_path_in_an_extends_options_must_be_relative_or_rooted)); + errors.push(createCompilerDiagnostic(Diagnostics.A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not, extendedConfig)); return; } let extendedConfigPath = toPath(extendedConfig, basePath, getCanonicalFileName); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a163a3bb641..68fe659e81f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3142,7 +3142,7 @@ "category": "Error", "code": 18000 }, - "The path in an 'extends' options must be relative or rooted.": { + "A path in an 'extends' option must be relative or rooted, but '{0}' is not.": { "category": "Error", "code": 18001 }, diff --git a/src/harness/unittests/configurationExtension.ts b/src/harness/unittests/configurationExtension.ts index 4537dc77576..8e845925eb2 100644 --- a/src/harness/unittests/configurationExtension.ts +++ b/src/harness/unittests/configurationExtension.ts @@ -179,7 +179,7 @@ namespace ts { testFailure("can error when 'extends' is neither relative nor rooted.", "extends2.json", [{ code: 18001, category: DiagnosticCategory.Error, - messageText: `The path in an 'extends' options must be relative or rooted.` + messageText: `A path in an 'extends' option must be relative or rooted, but 'configs/base' is not.` }]); }); }); From c7b0083820740b3078b1ce5503dee06099eba5eb Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 16 Nov 2016 15:15:30 -0800 Subject: [PATCH 24/26] Test widen literal types of parameter properties --- ...alTypesWidenInParameterPosition.errors.txt | 15 ++++++++++++ .../literalTypesWidenInParameterPosition.js | 23 +++++++++++++++++++ .../literalTypesWidenInParameterPosition.ts | 8 +++++++ 3 files changed, 46 insertions(+) create mode 100644 tests/baselines/reference/literalTypesWidenInParameterPosition.errors.txt create mode 100644 tests/baselines/reference/literalTypesWidenInParameterPosition.js create mode 100644 tests/cases/conformance/types/literal/literalTypesWidenInParameterPosition.ts diff --git a/tests/baselines/reference/literalTypesWidenInParameterPosition.errors.txt b/tests/baselines/reference/literalTypesWidenInParameterPosition.errors.txt new file mode 100644 index 00000000000..69d7196b978 --- /dev/null +++ b/tests/baselines/reference/literalTypesWidenInParameterPosition.errors.txt @@ -0,0 +1,15 @@ +tests/cases/conformance/types/literal/literalTypesWidenInParameterPosition.ts(4,9): error TS2322: Type '5' is not assignable to type '1'. + + +==== tests/cases/conformance/types/literal/literalTypesWidenInParameterPosition.ts (1 errors) ==== + class D { + readonly noWiden = 1 + constructor(readonly widen = 2) { + this.noWiden = 5; // error + ~~~~~~~~~~~~ +!!! error TS2322: Type '5' is not assignable to type '1'. + this.widen = 6; // ok + } + } + new D(7); // ok + \ No newline at end of file diff --git a/tests/baselines/reference/literalTypesWidenInParameterPosition.js b/tests/baselines/reference/literalTypesWidenInParameterPosition.js new file mode 100644 index 00000000000..2c1d165b6ec --- /dev/null +++ b/tests/baselines/reference/literalTypesWidenInParameterPosition.js @@ -0,0 +1,23 @@ +//// [literalTypesWidenInParameterPosition.ts] +class D { + readonly noWiden = 1 + constructor(readonly widen = 2) { + this.noWiden = 5; // error + this.widen = 6; // ok + } +} +new D(7); // ok + + +//// [literalTypesWidenInParameterPosition.js] +var D = (function () { + function D(widen) { + if (widen === void 0) { widen = 2; } + this.widen = widen; + this.noWiden = 1; + this.noWiden = 5; // error + this.widen = 6; // ok + } + return D; +}()); +new D(7); // ok diff --git a/tests/cases/conformance/types/literal/literalTypesWidenInParameterPosition.ts b/tests/cases/conformance/types/literal/literalTypesWidenInParameterPosition.ts new file mode 100644 index 00000000000..f1d0a9b428a --- /dev/null +++ b/tests/cases/conformance/types/literal/literalTypesWidenInParameterPosition.ts @@ -0,0 +1,8 @@ +class D { + readonly noWiden = 1 + constructor(readonly widen = 2) { + this.noWiden = 5; // error + this.widen = 6; // ok + } +} +new D(7); // ok From 5dbe5f4fbdb7c91598441bffdee7577a71471c2c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 16 Nov 2016 15:24:42 -0800 Subject: [PATCH 25/26] Widen literal types of parameter properties --- src/compiler/checker.ts | 2 +- src/compiler/utilities.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7132aedb324..b466bf7afb6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14744,7 +14744,7 @@ namespace ts { function checkDeclarationInitializer(declaration: VariableLikeDeclaration) { const type = checkExpressionCached(declaration.initializer); return getCombinedNodeFlags(declaration) & NodeFlags.Const || - getCombinedModifierFlags(declaration) & ModifierFlags.Readonly || + getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration) || isTypeAssertion(declaration.initializer) ? type : getWidenedLiteralType(type); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index bfabb3b1e4a..546416884f1 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4497,7 +4497,7 @@ namespace ts { } } - export function isParameterPropertyDeclaration(node: ParameterDeclaration): boolean { + export function isParameterPropertyDeclaration(node: Node): boolean { return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent); } From 52ec508e27fccd8a1d7c601918b70417086b9798 Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Wed, 16 Nov 2016 16:01:16 -0800 Subject: [PATCH 26/26] Code fix for missing imports (#11768) * Add codefix for missing imports + tests * Re-order and cleanup * refactor * make tests pass * Make import specifier for new imports more comprehensive * Fix existing import cases * refactor * Fix multiple import statement case * add multiple code fixes and code action filtering and polishing * not using the generic verify method for import fixes. * Correct insert position for new imports * improve the code action filtering logic * Fix line ending issue * cache where we can --- src/compiler/checker.ts | 9 +- src/compiler/core.ts | 8 + src/compiler/diagnosticMessages.json | 12 + src/compiler/moduleNameResolver.ts | 2 +- src/compiler/types.ts | 4 +- src/harness/fourslash.ts | 38 +- src/services/codefixes/codeFixProvider.ts | 4 +- src/services/codefixes/fixes.ts | 3 +- src/services/codefixes/importFixes.ts | 591 ++++++++++++++++++ src/services/services.ts | 23 +- src/services/types.ts | 1 + tests/cases/fourslash/fourslash.ts | 1 + .../importNameCodeFixExistingImport0.ts | 10 + .../importNameCodeFixExistingImport1.ts | 11 + .../importNameCodeFixExistingImport10.ts | 21 + .../importNameCodeFixExistingImport11.ts | 20 + .../importNameCodeFixExistingImport12.ts | 12 + .../importNameCodeFixExistingImport2.ts | 16 + .../importNameCodeFixExistingImport3.ts | 18 + .../importNameCodeFixExistingImport4.ts | 14 + .../importNameCodeFixExistingImport5.ts | 12 + .../importNameCodeFixExistingImport6.ts | 13 + .../importNameCodeFixExistingImport7.ts | 10 + .../importNameCodeFixExistingImport8.ts | 12 + .../importNameCodeFixExistingImport9.ts | 17 + .../importNameCodeFixExistingImportEquals0.ts | 18 + .../importNameCodeFixNewImportAmbient0.ts | 15 + .../importNameCodeFixNewImportAmbient1.ts | 28 + .../importNameCodeFixNewImportAmbient2.ts | 21 + .../importNameCodeFixNewImportAmbient3.ts | 30 + .../importNameCodeFixNewImportBaseUrl0.ts | 19 + .../importNameCodeFixNewImportDefault0.ts | 12 + .../importNameCodeFixNewImportFile0.ts | 13 + .../importNameCodeFixNewImportFile1.ts | 18 + .../importNameCodeFixNewImportFile2.ts | 13 + .../importNameCodeFixNewImportNodeModules0.ts | 19 + .../importNameCodeFixNewImportNodeModules1.ts | 16 + .../importNameCodeFixNewImportNodeModules2.ts | 25 + .../importNameCodeFixNewImportNodeModules3.ts | 14 + .../importNameCodeFixNewImportPaths0.ts | 22 + .../importNameCodeFixNewImportPaths1.ts | 22 + .../importNameCodeFixNewImportRootDirs0.ts | 23 + .../importNameCodeFixNewImportTypeRoots0.ts | 22 + .../importNameCodeFixOptionalImport0.ts | 20 + .../importNameCodeFixOptionalImport1.ts | 20 + tests/cases/fourslash/server/codefix.ts | 2 +- 46 files changed, 1265 insertions(+), 9 deletions(-) create mode 100644 src/services/codefixes/importFixes.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport1.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport10.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport11.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport12.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport2.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport3.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport4.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport5.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport6.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport7.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport8.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImport9.ts create mode 100644 tests/cases/fourslash/importNameCodeFixExistingImportEquals0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportAmbient0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportAmbient1.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportAmbient2.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportAmbient3.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportBaseUrl0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportDefault0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportFile0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportFile1.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportFile2.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportNodeModules0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportNodeModules1.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportNodeModules2.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportNodeModules3.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportPaths0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportPaths1.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportRootDirs0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixNewImportTypeRoots0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixOptionalImport0.ts create mode 100644 tests/cases/fourslash/importNameCodeFixOptionalImport1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b466bf7afb6..3d25fc8bbbb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -104,10 +104,10 @@ namespace ts { getEmitResolver, getExportsOfModule: getExportsOfModuleAsArray, getAmbientModules, - getJsxElementAttributesType, getJsxIntrinsicTagNames, isOptionalParameter, + tryGetMemberInModuleExports, tryFindAmbientModuleWithoutAugmentations: moduleName => { // we deliberately exclude augmentations // since we are only interested in declarations of the module itself @@ -1483,6 +1483,13 @@ namespace ts { return symbolsToArray(getExportsOfModule(moduleSymbol)); } + function tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined { + const symbolTable = getExportsOfModule(moduleSymbol); + if (symbolTable) { + return symbolTable[memberName]; + } + } + function getExportsOfSymbol(symbol: Symbol): SymbolTable { return symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) : symbol.exports || emptySymbols; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 10509309e80..89057dd2939 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1391,6 +1391,14 @@ namespace ts { getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015 ? ModuleKind.ES2015 : ModuleKind.CommonJS; } + export function getEmitModuleResolutionKind(compilerOptions: CompilerOptions) { + let moduleResolution = compilerOptions.moduleResolution; + if (moduleResolution === undefined) { + moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic; + } + return moduleResolution; + } + /* @internal */ export function hasZeroOrOneAsteriskCharacter(str: string): boolean { let seenAsterisk = false; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 68fe659e81f..2dd4e76f8f4 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3190,5 +3190,17 @@ "Type '{0}' is not assignable to type '{1}'. Two different types with this name exist, but they are unrelated.": { "category": "Error", "code": 90010 + }, + "Import {0} from {1}": { + "category": "Message", + "code": 90013 + }, + "Change {0} to {1}": { + "category": "Message", + "code": 90014 + }, + "Add {0} to existing import declaration from {1}": { + "category": "Message", + "code": 90015 } } diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 2d7f1277776..0daca9156d1 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -61,7 +61,7 @@ namespace ts { return { resolvedModule: resolved && resolvedModuleFromResolved(resolved, isExternalLibraryImport), failedLookupLocations }; } - function moduleHasNonRelativeName(moduleName: string): boolean { + export function moduleHasNonRelativeName(moduleName: string): boolean { return !(isRootedDiskPath(moduleName) || isExternalModuleNameRelative(moduleName)); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index af46cb2173c..b5e3eefbb2d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2371,6 +2371,8 @@ namespace ts { isOptionalParameter(node: ParameterDeclaration): boolean; getAmbientModules(): Symbol[]; + tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined; + /* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol; // Should not be called directly. Should only be accessed through the Program instance. @@ -3190,7 +3192,7 @@ namespace ts { target?: ScriptTarget; traceResolution?: boolean; types?: string[]; - /** Paths used to used to compute primary types search locations */ + /** Paths used to compute primary types search locations */ typeRoots?: string[]; /*@internal*/ version?: boolean; /*@internal*/ watch?: boolean; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index f4fc7b8f009..1486ea2253f 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2046,6 +2046,34 @@ namespace FourSlash { } } + public verifyImportFixAtPosition(expectedTextArray: string[], errorCode?: number) { + const ranges = this.getRanges(); + if (ranges.length == 0) { + this.raiseError("At least one range should be specified in the testfile."); + } + + const codeFixes = this.getCodeFixes(errorCode); + + if (!codeFixes || codeFixes.length == 0) { + this.raiseError("No codefixes returned."); + } + + const actualTextArray: string[] = []; + const scriptInfo = this.languageServiceAdapterHost.getScriptInfo(codeFixes[0].changes[0].fileName); + const originalContent = scriptInfo.content; + for (const codeFix of codeFixes) { + this.applyEdits(codeFix.changes[0].fileName, codeFix.changes[0].textChanges, /*isFormattingEdit*/ false); + actualTextArray.push(this.normalizeNewlines(this.rangeText(ranges[0]))); + scriptInfo.updateContent(originalContent); + } + const sortedExpectedArray = ts.map(expectedTextArray, str => this.normalizeNewlines(str)).sort(); + const sortedActualArray = actualTextArray.sort(); + if (!ts.arrayIsEqualTo(sortedExpectedArray, sortedActualArray)) { + this.raiseError( + `Actual text array doesn't match expected text array. \nActual: \n"${sortedActualArray.join("\n\n")}"\n---\nExpected: \n'${sortedExpectedArray.join("\n\n")}'`); + } + } + public verifyDocCommentTemplate(expected?: ts.TextInsertion) { const name = "verifyDocCommentTemplate"; const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition); @@ -2079,6 +2107,10 @@ namespace FourSlash { }); } + private normalizeNewlines(str: string) { + return str.replace(/\r?\n/g, "\n"); + } + public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) { const openBraceMap = ts.createMap({ @@ -2606,7 +2638,7 @@ ${code} resetLocalData(); } - currentFileName = basePath + "/" + value; + currentFileName = ts.isRootedDiskPath(value) ? value : basePath + "/" + value; currentFileOptions[key] = value; } else { @@ -3303,6 +3335,10 @@ namespace FourSlashInterface { this.state.verifyCodeFixAtPosition(expectedText, errorCode); } + public importFixAtPosition(expectedTextArray: string[], errorCode?: number): void { + this.state.verifyImportFixAtPosition(expectedTextArray, errorCode); + } + public navigationBar(json: any) { this.state.verifyNavigationBar(json); } diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts index c61cbe1b19e..e4489fc2dca 100644 --- a/src/services/codefixes/codeFixProvider.ts +++ b/src/services/codefixes/codeFixProvider.ts @@ -1,4 +1,4 @@ -/* @internal */ +/* @internal */ namespace ts { export interface CodeFix { errorCodes: number[]; @@ -11,6 +11,8 @@ namespace ts { span: TextSpan; program: Program; newLineCharacter: string; + host: LanguageServiceHost; + cancellationToken: CancellationToken; } export namespace codefix { diff --git a/src/services/codefixes/fixes.ts b/src/services/codefixes/fixes.ts index ae13a965838..a38b196cb00 100644 --- a/src/services/codefixes/fixes.ts +++ b/src/services/codefixes/fixes.ts @@ -1,2 +1,3 @@ /// -/// \ No newline at end of file +/// +/// diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts new file mode 100644 index 00000000000..4adda19689d --- /dev/null +++ b/src/services/codefixes/importFixes.ts @@ -0,0 +1,591 @@ +/* @internal */ +namespace ts.codefix { + + type ImportCodeActionKind = "CodeChange" | "InsertingIntoExistingImport" | "NewImport"; + interface ImportCodeAction extends CodeAction { + kind: ImportCodeActionKind, + moduleSpecifier?: string + } + + enum ModuleSpecifierComparison { + Better, + Equal, + Worse + } + + class ImportCodeActionMap { + private symbolIdToActionMap = createMap(); + + addAction(symbolId: number, newAction: ImportCodeAction) { + if (!newAction) { + return; + } + + if (!this.symbolIdToActionMap[symbolId]) { + this.symbolIdToActionMap[symbolId] = [newAction]; + return; + } + + if (newAction.kind === "CodeChange") { + this.symbolIdToActionMap[symbolId].push(newAction); + return; + } + + const updatedNewImports: ImportCodeAction[] = []; + for (const existingAction of this.symbolIdToActionMap[symbolId]) { + if (existingAction.kind === "CodeChange") { + // only import actions should compare + updatedNewImports.push(existingAction); + continue; + } + + switch (this.compareModuleSpecifiers(existingAction.moduleSpecifier, newAction.moduleSpecifier)) { + case ModuleSpecifierComparison.Better: + // the new one is not worth considering if it is a new improt. + // However if it is instead a insertion into existing import, the user might want to use + // the module specifier even it is worse by our standards. So keep it. + if (newAction.kind === "NewImport") { + return; + } + case ModuleSpecifierComparison.Equal: + // the current one is safe. But it is still possible that the new one is worse + // than another existing one. For example, you may have new imports from "./foo/bar" + // and "bar", when the new one is "bar/bar2" and the current one is "./foo/bar". The new + // one and the current one are not comparable (one relative path and one absolute path), + // but the new one is worse than the other one, so should not add to the list. + updatedNewImports.push(existingAction); + break; + case ModuleSpecifierComparison.Worse: + // the existing one is worse, remove from the list. + continue; + } + } + // if we reach here, it means the new one is better or equal to all of the existing ones. + updatedNewImports.push(newAction); + this.symbolIdToActionMap[symbolId] = updatedNewImports; + } + + addActions(symbolId: number, newActions: ImportCodeAction[]) { + for (const newAction of newActions) { + this.addAction(symbolId, newAction); + } + } + + getAllActions() { + let result: ImportCodeAction[] = []; + for (const symbolId in this.symbolIdToActionMap) { + result = concatenate(result, this.symbolIdToActionMap[symbolId]); + } + return result; + } + + private compareModuleSpecifiers(moduleSpecifier1: string, moduleSpecifier2: string): ModuleSpecifierComparison { + if (moduleSpecifier1 === moduleSpecifier2) { + return ModuleSpecifierComparison.Equal; + } + + // if moduleSpecifier1 (ms1) is a substring of ms2, then it is better + if (moduleSpecifier2.indexOf(moduleSpecifier1) === 0) { + return ModuleSpecifierComparison.Better; + } + + if (moduleSpecifier1.indexOf(moduleSpecifier2) === 0) { + return ModuleSpecifierComparison.Worse; + } + + // if both are relative paths, and ms1 has fewer levels, then it is better + if (isExternalModuleNameRelative(moduleSpecifier1) && isExternalModuleNameRelative(moduleSpecifier2)) { + const regex = new RegExp(directorySeparator, "g"); + const moduleSpecifier1LevelCount = (moduleSpecifier1.match(regex) || []).length; + const moduleSpecifier2LevelCount = (moduleSpecifier2.match(regex) || []).length; + + return moduleSpecifier1LevelCount < moduleSpecifier2LevelCount + ? ModuleSpecifierComparison.Better + : moduleSpecifier1LevelCount === moduleSpecifier2LevelCount + ? ModuleSpecifierComparison.Equal + : ModuleSpecifierComparison.Worse; + } + + // the equal cases include when the two specifiers are not comparable. + return ModuleSpecifierComparison.Equal; + } + } + + registerCodeFix({ + errorCodes: [Diagnostics.Cannot_find_name_0.code], + getCodeActions: (context: CodeFixContext) => { + const sourceFile = context.sourceFile; + const checker = context.program.getTypeChecker(); + const allSourceFiles = context.program.getSourceFiles(); + const useCaseSensitiveFileNames = context.host.useCaseSensitiveFileNames ? context.host.useCaseSensitiveFileNames() : false; + + const token = getTokenAtPosition(sourceFile, context.span.start); + const name = token.getText(); + const symbolIdActionMap = new ImportCodeActionMap(); + + // this is a module id -> module import declaration map + const cachedImportDeclarations = createMap<(ImportDeclaration | ImportEqualsDeclaration)[]>(); + let cachedNewImportInsertPosition: number; + + const allPotentialModules = checker.getAmbientModules(); + for (const otherSourceFile of allSourceFiles) { + if (otherSourceFile !== sourceFile && isExternalOrCommonJsModule(otherSourceFile)) { + allPotentialModules.push(otherSourceFile.symbol); + } + } + + const currentTokenMeaning = getMeaningFromLocation(token); + for (const moduleSymbol of allPotentialModules) { + context.cancellationToken.throwIfCancellationRequested(); + + // check the default export + const defaultExport = checker.tryGetMemberInModuleExports("default", moduleSymbol); + if (defaultExport) { + const localSymbol = getLocalSymbolForExportDefault(defaultExport); + if (localSymbol && localSymbol.name === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) { + // check if this symbol is already used + const symbolId = getUniqueSymbolId(localSymbol); + symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, /*isDefaultExport*/ true)); + } + } + + // check exports with the same name + const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExports(name, moduleSymbol); + if (exportSymbolWithIdenticalName && checkSymbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) { + const symbolId = getUniqueSymbolId(exportSymbolWithIdenticalName); + symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol)); + } + } + + return symbolIdActionMap.getAllActions(); + + function getImportDeclarations(moduleSymbol: Symbol) { + const moduleSymbolId = getUniqueSymbolId(moduleSymbol); + + if (cachedImportDeclarations[moduleSymbolId]) { + return cachedImportDeclarations[moduleSymbolId]; + } + + const existingDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[] = []; + for (const importModuleSpecifier of sourceFile.imports) { + const importSymbol = checker.getSymbolAtLocation(importModuleSpecifier); + if (importSymbol === moduleSymbol) { + existingDeclarations.push(getImportDeclaration(importModuleSpecifier)); + } + } + cachedImportDeclarations[moduleSymbolId] = existingDeclarations; + return existingDeclarations; + + function getImportDeclaration(moduleSpecifier: LiteralExpression) { + let node: Node = moduleSpecifier; + while (node) { + if (node.kind === SyntaxKind.ImportDeclaration) { + return node; + } + if (node.kind === SyntaxKind.ImportEqualsDeclaration) { + return node; + } + node = node.parent; + } + return undefined; + } + } + + function getUniqueSymbolId(symbol: Symbol) { + if (symbol.flags & SymbolFlags.Alias) { + return getSymbolId(checker.getAliasedSymbol(symbol)); + } + return getSymbolId(symbol); + } + + function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) { + const declarations = symbol.getDeclarations(); + return declarations ? some(symbol.declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)) : false; + } + + function getCodeActionForImport(moduleSymbol: Symbol, isDefault?: boolean): ImportCodeAction[] { + const existingDeclarations = getImportDeclarations(moduleSymbol); + if (existingDeclarations.length > 0) { + // With an existing import statement, there are more than one actions the user can do. + return getCodeActionsForExistingImport(existingDeclarations); + } + else { + return [getCodeActionForNewImport()]; + } + + + + function getCodeActionsForExistingImport(declarations: (ImportDeclaration | ImportEqualsDeclaration)[]): ImportCodeAction[] { + const actions: ImportCodeAction[] = []; + + // It is possible that multiple import statements with the same specifier exist in the file. + // e.g. + // + // import * as ns from "foo"; + // import { member1, member2 } from "foo"; + // + // member3/**/ <-- cusor here + // + // in this case we should provie 2 actions: + // 1. change "member3" to "ns.member3" + // 2. add "member3" to the second import statement's import list + // and it is up to the user to decide which one fits best. + let namespaceImportDeclaration: ImportDeclaration | ImportEqualsDeclaration; + let namedImportDeclaration: ImportDeclaration; + let existingModuleSpecifier: string; + for (const declaration of declarations) { + if (declaration.kind === SyntaxKind.ImportDeclaration) { + const namedBindings = declaration.importClause && declaration.importClause.namedBindings; + if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) { + // case: + // import * as ns from "foo" + namespaceImportDeclaration = declaration; + } + else { + // cases: + // import default from "foo" + // import { bar } from "foo" or combination with the first one + // import "foo" + namedImportDeclaration = declaration; + } + existingModuleSpecifier = declaration.moduleSpecifier.getText(); + } + else { + // case: + // import foo = require("foo") + namespaceImportDeclaration = declaration; + existingModuleSpecifier = getModuleSpecifierFromImportEqualsDeclaration(declaration); + } + } + + if (namespaceImportDeclaration) { + actions.push(getCodeActionForNamespaceImport(namespaceImportDeclaration)); + } + + if (namedImportDeclaration && namedImportDeclaration.importClause && + (namedImportDeclaration.importClause.name || namedImportDeclaration.importClause.namedBindings)) { + /** + * If the existing import declaration already has a named import list, just + * insert the identifier into that list. + */ + const textChange = getTextChangeForImportClause(namedImportDeclaration.importClause); + const moduleSpecifierWithoutQuotes = stripQuotes(namedImportDeclaration.moduleSpecifier.getText()); + actions.push(createCodeAction( + Diagnostics.Add_0_to_existing_import_declaration_from_1, + [name, moduleSpecifierWithoutQuotes], + textChange.newText, + textChange.span, + sourceFile.fileName, + "InsertingIntoExistingImport", + moduleSpecifierWithoutQuotes + )); + } + else { + // we need to create a new import statement, but the existing module specifier can be reused. + actions.push(getCodeActionForNewImport(existingModuleSpecifier)); + } + return actions; + + function getModuleSpecifierFromImportEqualsDeclaration(declaration: ImportEqualsDeclaration) { + if (declaration.moduleReference && declaration.moduleReference.kind === SyntaxKind.ExternalModuleReference) { + return declaration.moduleReference.expression.getText(); + } + return declaration.moduleReference.getText(); + } + + function getTextChangeForImportClause(importClause: ImportClause): TextChange { + const newImportText = isDefault ? `default as ${name}` : name; + const importList = importClause.namedBindings; + // case 1: + // original text: import default from "module" + // change to: import default, { name } from "module" + if (!importList && importClause.name) { + const start = importClause.name.getEnd(); + return { + newText: `, { ${newImportText} }`, + span: { start, length: 0 } + }; + } + + // case 2: + // original text: import {} from "module" + // change to: import { name } from "module" + if (importList.elements.length === 0) { + const start = importList.getStart(); + return { + newText: `{ ${newImportText} }`, + span: { start, length: importList.getEnd() - start } + }; + } + + // case 3: + // original text: import { foo, bar } from "module" + // change to: import { foo, bar, name } from "module" + const insertPoint = importList.elements[importList.elements.length - 1].getEnd(); + /** + * If the import list has one import per line, preserve that. Otherwise, insert on same line as last element + * import { + * foo + * } from "./module"; + */ + const startLine = getLineOfLocalPosition(sourceFile, importList.getStart()); + const endLine = getLineOfLocalPosition(sourceFile, importList.getEnd()); + const oneImportPerLine = endLine - startLine > importList.elements.length; + + return { + newText: `,${oneImportPerLine ? context.newLineCharacter : " "}${newImportText}`, + span: { start: insertPoint, length: 0 } + }; + } + + function getCodeActionForNamespaceImport(declaration: ImportDeclaration | ImportEqualsDeclaration): ImportCodeAction { + let namespacePrefix: string; + if (declaration.kind === SyntaxKind.ImportDeclaration) { + namespacePrefix = (declaration.importClause.namedBindings).name.getText(); + } + else { + namespacePrefix = declaration.name.getText(); + } + namespacePrefix = stripQuotes(namespacePrefix); + + /** + * Cases: + * import * as ns from "mod" + * import default, * as ns from "mod" + * import ns = require("mod") + * + * Because there is no import list, we alter the reference to include the + * namespace instead of altering the import declaration. For example, "foo" would + * become "ns.foo" + */ + return createCodeAction( + Diagnostics.Change_0_to_1, + [name, `${namespacePrefix}.${name}`], + `${namespacePrefix}.`, + { start: token.getStart(), length: 0 }, + sourceFile.fileName, + "CodeChange" + ); + } + } + + function getCodeActionForNewImport(moduleSpecifier?: string): ImportCodeAction { + if (!cachedNewImportInsertPosition) { + // insert after any existing imports + let lastModuleSpecifierEnd = -1; + for (const moduleSpecifier of sourceFile.imports) { + const end = moduleSpecifier.getEnd(); + if (!lastModuleSpecifierEnd || end > lastModuleSpecifierEnd) { + lastModuleSpecifierEnd = end; + } + } + cachedNewImportInsertPosition = lastModuleSpecifierEnd > 0 ? sourceFile.getLineEndOfPosition(lastModuleSpecifierEnd) : sourceFile.getStart(); + } + + const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); + const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier || getModuleSpecifierForNewImport()); + const importStatementText = isDefault + ? `import ${name} from "${moduleSpecifierWithoutQuotes}"` + : `import { ${name} } from "${moduleSpecifierWithoutQuotes}"`; + + // if this file doesn't have any import statements, insert an import statement and then insert a new line + // between the only import statement and user code. Otherwise just insert the statement because chances + // are there are already a new line seperating code and import statements. + const newText = cachedNewImportInsertPosition === sourceFile.getStart() + ? `${importStatementText};${context.newLineCharacter}${context.newLineCharacter}` + : `${context.newLineCharacter}${importStatementText};`; + + return createCodeAction( + Diagnostics.Import_0_from_1, + [name, `"${moduleSpecifierWithoutQuotes}"`], + newText, + { start: cachedNewImportInsertPosition, length: 0 }, + sourceFile.fileName, + "NewImport", + moduleSpecifierWithoutQuotes + ); + + function getModuleSpecifierForNewImport() { + const fileName = sourceFile.path; + const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().path; + const sourceDirectory = getDirectoryPath(fileName); + const options = context.program.getCompilerOptions(); + + return tryGetModuleNameFromAmbientModule() || + tryGetModuleNameFromBaseUrl() || + tryGetModuleNameFromRootDirs() || + tryGetModuleNameFromTypeRoots() || + tryGetModuleNameAsNodeModule() || + removeFileExtension(getRelativePath(moduleFileName, sourceDirectory)); + + function tryGetModuleNameFromAmbientModule(): string { + if (moduleSymbol.valueDeclaration.kind !== SyntaxKind.SourceFile) { + return moduleSymbol.name; + } + } + + function tryGetModuleNameFromBaseUrl() { + if (!options.baseUrl) { + return undefined; + } + + const normalizedBaseUrl = toPath(options.baseUrl, getDirectoryPath(options.baseUrl), getCanonicalFileName); + let relativeName = tryRemoveParentDirectoryName(moduleFileName, normalizedBaseUrl); + if (!relativeName) { + return undefined; + } + + relativeName = removeExtensionAndIndexPostFix(relativeName); + + if (options.paths) { + for (const key in options.paths) { + for (const pattern of options.paths[key]) { + const indexOfStar = pattern.indexOf("*"); + if (indexOfStar === 0 && pattern.length === 1) { + continue; + } + else if (indexOfStar !== -1) { + const prefix = pattern.substr(0, indexOfStar); + const suffix = pattern.substr(indexOfStar + 1); + if (relativeName.length >= prefix.length + suffix.length && + startsWith(relativeName, prefix) && + endsWith(relativeName, suffix)) { + const matchedStar = relativeName.substr(prefix.length, relativeName.length - suffix.length); + return key.replace("\*", matchedStar); + } + } + else if (pattern === relativeName) { + return key; + } + } + } + } + + return relativeName; + } + + function tryGetModuleNameFromRootDirs() { + if (options.rootDirs) { + const normalizedRootDirs = map(options.rootDirs, rootDir => toPath(rootDir, /*basePath*/ undefined, getCanonicalFileName)); + const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, normalizedRootDirs); + const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, normalizedRootDirs); + if (normalizedTargetPath !== undefined) { + const relativePath = normalizedSourcePath !== undefined ? getRelativePath(normalizedTargetPath, normalizedSourcePath) : normalizedTargetPath; + return removeFileExtension(relativePath); + } + } + return undefined; + } + + function tryGetModuleNameFromTypeRoots() { + const typeRoots = getEffectiveTypeRoots(options, context.host); + if (typeRoots) { + const normalizedTypeRoots = map(typeRoots, typeRoot => toPath(typeRoot, /*basePath*/ undefined, getCanonicalFileName)); + for (const typeRoot of normalizedTypeRoots) { + if (startsWith(moduleFileName, typeRoot)) { + let relativeFileName = moduleFileName.substring(typeRoot.length + 1); + return removeExtensionAndIndexPostFix(relativeFileName); + } + } + } + } + + function tryGetModuleNameAsNodeModule() { + if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) { + // nothing to do here + return undefined; + } + + const indexOfNodeModules = moduleFileName.indexOf("node_modules"); + if (indexOfNodeModules < 0) { + return undefined; + } + + let relativeFileName: string; + if (sourceDirectory.indexOf(moduleFileName.substring(0, indexOfNodeModules - 1)) === 0) { + // if node_modules folder is in this folder or any of its parent folder, no need to keep it. + relativeFileName = moduleFileName.substring(indexOfNodeModules + 13 /* "node_modules\".length */); + } + else { + relativeFileName = getRelativePath(moduleFileName, sourceDirectory); + } + + relativeFileName = removeFileExtension(relativeFileName); + if (endsWith(relativeFileName, "/index")) { + relativeFileName = getDirectoryPath(relativeFileName); + } + else { + try { + const moduleDirectory = getDirectoryPath(moduleFileName); + const packageJsonContent = JSON.parse(context.host.readFile(combinePaths(moduleDirectory, "package.json"))); + if (packageJsonContent) { + const mainFile = packageJsonContent.main || packageJsonContent.typings; + if (mainFile) { + const mainExportFile = toPath(mainFile, moduleDirectory, getCanonicalFileName); + if (removeFileExtension(mainExportFile) === removeFileExtension(moduleFileName)) { + relativeFileName = getDirectoryPath(relativeFileName); + } + } + } + } + catch (e) { } + } + + return relativeFileName; + } + } + + function getPathRelativeToRootDirs(path: Path, rootDirs: Path[]) { + for (const rootDir of rootDirs) { + const relativeName = tryRemoveParentDirectoryName(path, rootDir); + if (relativeName !== undefined) { + return relativeName; + } + } + return undefined; + } + + function removeExtensionAndIndexPostFix(fileName: string) { + fileName = removeFileExtension(fileName); + if (endsWith(fileName, "/index")) { + fileName = fileName.substr(0, fileName.length - 6/* "/index".length */); + } + return fileName; + } + + function getRelativePath(path: string, directoryPath: string) { + const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, false); + return moduleHasNonRelativeName(relativePath) ? "./" + relativePath : relativePath; + } + + function tryRemoveParentDirectoryName(path: Path, parentDirectory: Path) { + const index = path.indexOf(parentDirectory); + if (index === 0) { + return endsWith(parentDirectory, directorySeparator) + ? path.substring(parentDirectory.length) + : path.substring(parentDirectory.length + 1); + } + return undefined; + } + } + + } + + function createCodeAction( + description: DiagnosticMessage, + diagnosticArgs: string[], + newText: string, + span: TextSpan, + fileName: string, + kind: ImportCodeActionKind, + moduleSpecifier?: string): ImportCodeAction { + return { + description: formatMessage.apply(undefined, [undefined, description].concat(diagnosticArgs)), + changes: [{ fileName, textChanges: [{ newText, span }] }], + kind, + moduleSpecifier + }; + } + } + }); +} diff --git a/src/services/services.ts b/src/services/services.ts index 3c0b7838314..ef50f4b5a2f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1,4 +1,4 @@ -/// +/// /// /// @@ -492,6 +492,23 @@ namespace ts { return ts.getPositionOfLineAndCharacter(this, line, character); } + public getLineEndOfPosition(pos: number): number { + const { line } = this.getLineAndCharacterOfPosition(pos); + const lineStarts = this.getLineStarts(); + + let lastCharPos: number; + if (line + 1 >= lineStarts.length) { + lastCharPos = this.getEnd(); + } + if (!lastCharPos) { + lastCharPos = lineStarts[line + 1] - 1; + } + + const fullText = this.getFullText(); + // if the new line is "\r\n", we should return the last non-new-line-character position + return fullText[lastCharPos] === "\n" && fullText[lastCharPos - 1] === "\r" ? lastCharPos - 1 : lastCharPos; + } + public getNamedDeclarations(): Map { if (!this.namedDeclarations) { this.namedDeclarations = this.computeNamedDeclarations(); @@ -1676,7 +1693,9 @@ namespace ts { sourceFile: sourceFile, span: span, program: program, - newLineCharacter: newLineChar + newLineCharacter: newLineChar, + host: host, + cancellationToken: cancellationToken }; const fixes = codefix.getFixes(context); diff --git a/src/services/types.ts b/src/services/types.ts index 4e04df3fc7c..6a0e6e886b5 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -53,6 +53,7 @@ namespace ts { /* @internal */ getNamedDeclarations(): Map; getLineAndCharacterOfPosition(pos: number): LineAndCharacter; + getLineEndOfPosition(pos: number): number; getLineStarts(): number[]; getPositionOfLineAndCharacter(line: number, character: number): number; update(newText: string, textChangeRange: TextChangeRange): SourceFile; diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 295b8e422b9..35c68b49fd8 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -211,6 +211,7 @@ declare namespace FourSlashInterface { DocCommentTemplate(expectedText: string, expectedOffset: number, empty?: boolean): void; noDocCommentTemplate(): void; codeFixAtPosition(expectedText: string, errorCode?: number): void; + importFixAtPosition(expectedTextArray: string[], errorCode?: number): void; navigationBar(json: any): void; navigationTree(json: any): void; diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport0.ts b/tests/cases/fourslash/importNameCodeFixExistingImport0.ts new file mode 100644 index 00000000000..5e5be220688 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport0.ts @@ -0,0 +1,10 @@ +/// + +//// import [|{ v1 }|] from "./module"; +//// f1/*0*/(); + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; + +verify.importFixAtPosition([`{ v1, f1 }`]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport1.ts b/tests/cases/fourslash/importNameCodeFixExistingImport1.ts new file mode 100644 index 00000000000..9571d0fcf57 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport1.ts @@ -0,0 +1,11 @@ +/// + +//// import d, [|{ v1 }|] from "./module"; +//// f1/*0*/(); + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; +//// export default var d1 = 6; + +verify.importFixAtPosition([`{ v1, f1 }`]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport10.ts b/tests/cases/fourslash/importNameCodeFixExistingImport10.ts new file mode 100644 index 00000000000..25246e70123 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport10.ts @@ -0,0 +1,21 @@ +/// + +//// import [|{ +//// v1, +//// v2 +//// }|] from "./module"; +//// f1/*0*/(); + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; +//// export var v2 = 5; +//// export var v3 = 5; + +verify.importFixAtPosition([ +`{ + v1, + v2, +f1 +}` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport11.ts b/tests/cases/fourslash/importNameCodeFixExistingImport11.ts new file mode 100644 index 00000000000..304ffb896df --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport11.ts @@ -0,0 +1,20 @@ +/// + +////import [|{ +//// v1, v2, +//// v3 +////}|] from "./module"; +////f1/*0*/(); + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; +//// export var v2 = 5; +//// export var v3 = 5; + +verify.importFixAtPosition([ +`{ + v1, v2, + v3, f1 +}` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport12.ts b/tests/cases/fourslash/importNameCodeFixExistingImport12.ts new file mode 100644 index 00000000000..e00dee504c5 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport12.ts @@ -0,0 +1,12 @@ +/// + +//// import [|{}|] from "./module"; +//// f1/*0*/(); + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; +//// export var v2 = 5; +//// export var v3 = 5; + +verify.importFixAtPosition([`{ f1 }`]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport2.ts b/tests/cases/fourslash/importNameCodeFixExistingImport2.ts new file mode 100644 index 00000000000..6a92976f4ef --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport2.ts @@ -0,0 +1,16 @@ +/// + +//// [|import * as ns from "./module"; +//// f1/*0*/();|] + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; + +verify.importFixAtPosition([ +`import * as ns from "./module"; +import { f1 } from "./module"; +f1();`, +`import * as ns from "./module"; +ns.f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport3.ts b/tests/cases/fourslash/importNameCodeFixExistingImport3.ts new file mode 100644 index 00000000000..bc00e8d420a --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport3.ts @@ -0,0 +1,18 @@ +/// + +//// [|import d, * as ns from "./module" ; +//// f1/*0*/();|] + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; +//// export default var d1 = 6; + +// Test with some extra spaces before the semicolon +verify.importFixAtPosition([ +`import d, * as ns from "./module" ; +ns.f1();`, +`import d, * as ns from "./module" ; +import { f1 } from "./module"; +f1();`, +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport4.ts b/tests/cases/fourslash/importNameCodeFixExistingImport4.ts new file mode 100644 index 00000000000..d93cb73664e --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport4.ts @@ -0,0 +1,14 @@ +/// + +//// [|import d from "./module"; +//// f1/*0*/();|] + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; +//// export default var d1 = 6; + +verify.importFixAtPosition([ +`import d, { f1 } from "./module"; +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport5.ts b/tests/cases/fourslash/importNameCodeFixExistingImport5.ts new file mode 100644 index 00000000000..ed9297124d9 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport5.ts @@ -0,0 +1,12 @@ +/// + +//// [|import "./module"; +//// f1/*0*/();|] + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; + +verify.importFixAtPosition([`import "./module"; +import { f1 } from "./module"; +f1();`]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport6.ts b/tests/cases/fourslash/importNameCodeFixExistingImport6.ts new file mode 100644 index 00000000000..7ae157a51ce --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport6.ts @@ -0,0 +1,13 @@ +/// + +//// import [|{ v1 }|] from "fake-module"; +//// f1/*0*/(); + +// @Filename: ../package.json +//// { "dependencies": { "fake-module": "latest" } } + +// @Filename: ../node_modules/fake-module/index.ts +//// export var v1 = 5; +//// export function f1(); + +verify.importFixAtPosition([`{ v1, f1 }`]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport7.ts b/tests/cases/fourslash/importNameCodeFixExistingImport7.ts new file mode 100644 index 00000000000..249929eabc7 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport7.ts @@ -0,0 +1,10 @@ +/// + +//// import [|{ v1 }|] from "../other_dir/module"; +//// f1/*0*/(); + +// @Filename: ../other_dir/module.ts +//// export var v1 = 5; +//// export function f1(); + +verify.importFixAtPosition([`{ v1, f1 }`]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport8.ts b/tests/cases/fourslash/importNameCodeFixExistingImport8.ts new file mode 100644 index 00000000000..da7beaa0a47 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport8.ts @@ -0,0 +1,12 @@ +/// + +//// import [|{v1, v2, v3,}|] from "./module"; +//// f1/*0*/(); + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; +//// export var v2 = 5; +//// export var v3 = 5; + +verify.importFixAtPosition([`{v1, v2, v3, f1,}`]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport9.ts b/tests/cases/fourslash/importNameCodeFixExistingImport9.ts new file mode 100644 index 00000000000..05d17927454 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImport9.ts @@ -0,0 +1,17 @@ +/// + +//// import [|{ +//// v1 +//// }|] from "./module"; +//// f1/*0*/(); + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; + +verify.importFixAtPosition([ +`{ + v1, +f1 +}` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixExistingImportEquals0.ts b/tests/cases/fourslash/importNameCodeFixExistingImportEquals0.ts new file mode 100644 index 00000000000..f431e6356d1 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixExistingImportEquals0.ts @@ -0,0 +1,18 @@ +/// + +//// [|import ns = require("ambient-module"); +//// var x = v1/*0*/ + 5;|] + +// @Filename: ambientModule.ts +//// declare module "ambient-module" { +//// export function f1(); +//// export var v1; +//// } + +verify.importFixAtPosition([ +`import ns = require("ambient-module"); +var x = ns.v1 + 5;`, +`import ns = require("ambient-module"); +import { v1 } from "ambient-module"; +var x = v1 + 5;`, +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportAmbient0.ts b/tests/cases/fourslash/importNameCodeFixNewImportAmbient0.ts new file mode 100644 index 00000000000..1d7b5bc3e7f --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportAmbient0.ts @@ -0,0 +1,15 @@ +/// + +//// [|f1/*0*/();|] + +// @Filename: ambientModule.ts +//// declare module "ambient-module" { +//// export function f1(); +//// export var v1; +//// } + +verify.importFixAtPosition([ +`import { f1 } from "ambient-module"; + +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportAmbient1.ts b/tests/cases/fourslash/importNameCodeFixNewImportAmbient1.ts new file mode 100644 index 00000000000..60504c89711 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportAmbient1.ts @@ -0,0 +1,28 @@ +/// + +//// import d from "other-ambient-module"; +//// [|import * as ns from "yet-another-ambient-module"; +//// var x = v1/*0*/ + 5;|] + +// @Filename: ambientModule.ts +//// declare module "ambient-module" { +//// export function f1(); +//// export var v1; +//// } + +// @Filename: otherAmbientModule.ts +//// declare module "other-ambient-module" { +//// export default function f2(); +//// } + +// @Filename: yetAnotherAmbientModule.ts +//// declare module "yet-another-ambient-module" { +//// export function f3(); +//// export var v3; +//// } + +verify.importFixAtPosition([ +`import * as ns from "yet-another-ambient-module"; +import { v1 } from "ambient-module"; +var x = v1 + 5;` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportAmbient2.ts b/tests/cases/fourslash/importNameCodeFixNewImportAmbient2.ts new file mode 100644 index 00000000000..999da4bffbb --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportAmbient2.ts @@ -0,0 +1,21 @@ +/// + +////[|/* +//// * I'm a license or something +//// */ +////f1/*0*/();|] + +// @Filename: ambientModule.ts +//// declare module "ambient-module" { +//// export function f1(); +//// export var v1; +//// } + +verify.importFixAtPosition([ +`/* + * I'm a license or something + */ +import { f1 } from "ambient-module"; + +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportAmbient3.ts b/tests/cases/fourslash/importNameCodeFixNewImportAmbient3.ts new file mode 100644 index 00000000000..648293cce2e --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportAmbient3.ts @@ -0,0 +1,30 @@ +/// + +//// let a = "I am a non-trivial statement that appears before imports"; +//// import d from "other-ambient-module" +//// [|import * as ns from "yet-another-ambient-module" +//// var x = v1/*0*/ + 5;|] + +// @Filename: ambientModule.ts +//// declare module "ambient-module" { +//// export function f1(); +//// export var v1; +//// } + +// @Filename: otherAmbientModule.ts +//// declare module "other-ambient-module" { +//// export default function f2(); +//// } + +// @Filename: yetAnotherAmbientModule.ts +//// declare module "yet-another-ambient-module" { +//// export function f3(); +//// export var v3; +//// } + +// test cases when there are no semicolons at the line end +verify.importFixAtPosition([ +`import * as ns from "yet-another-ambient-module" +import { v1 } from "ambient-module"; +var x = v1 + 5;` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl0.ts b/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl0.ts new file mode 100644 index 00000000000..e15c2cf4399 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl0.ts @@ -0,0 +1,19 @@ +/// + +//// [|f1/*0*/();|] + +// @Filename: tsconfig.json +//// { +//// "compilerOptions": { +//// "baseUrl": "./a" +//// } +//// } + +// @Filename: a/b.ts +//// export function f1() { }; + +verify.importFixAtPosition([ +`import { f1 } from "b"; + +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportDefault0.ts b/tests/cases/fourslash/importNameCodeFixNewImportDefault0.ts new file mode 100644 index 00000000000..3efe023e922 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportDefault0.ts @@ -0,0 +1,12 @@ +/// + +//// [|f1/*0*/();|] + +// @Filename: module.ts +//// export default function f1() { }; + +verify.importFixAtPosition([ +`import f1 from "./module"; + +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFile0.ts b/tests/cases/fourslash/importNameCodeFixNewImportFile0.ts new file mode 100644 index 00000000000..2372110437a --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFile0.ts @@ -0,0 +1,13 @@ +/// + +//// [|f1/*0*/();|] + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; + +verify.importFixAtPosition([ +`import { f1 } from "./module"; + +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFile1.ts b/tests/cases/fourslash/importNameCodeFixNewImportFile1.ts new file mode 100644 index 00000000000..0223d96e018 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFile1.ts @@ -0,0 +1,18 @@ +/// + +//// [|/// +//// f1/*0*/();|] + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; + +// @Filename: tripleSlashReference.ts +//// var x = 5;/*dummy*/ + +verify.importFixAtPosition([ +`/// +import { f1 } from "./module"; + +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFile2.ts b/tests/cases/fourslash/importNameCodeFixNewImportFile2.ts new file mode 100644 index 00000000000..ca9330e9846 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFile2.ts @@ -0,0 +1,13 @@ +/// + +//// [|f1/*0*/();|] + +// @Filename: ../../other_dir/module.ts +//// export var v1 = 5; +//// export function f1(); + +verify.importFixAtPosition([ +`import { f1 } from "../../other_dir/module"; + +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportNodeModules0.ts b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules0.ts new file mode 100644 index 00000000000..6013f865ddf --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules0.ts @@ -0,0 +1,19 @@ +/// + +//// [|f1/*0*/();|] + +// @Filename: ../package.json +//// { "dependencies": { "fake-module": "latest" } } + +// @Filename: ../node_modules/fake-module/index.ts +//// export var v1 = 5; +//// export function f1(); + +// @Filename: ../node_modules/fake-module/package.json +//// {} + +verify.importFixAtPosition([ +`import { f1 } from "fake-module"; + +f1();` +]); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportNodeModules1.ts b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules1.ts new file mode 100644 index 00000000000..6bffe41b27a --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules1.ts @@ -0,0 +1,16 @@ +/// + +//// [|f1/*0*/();|] + +// @Filename: ../package.json +//// { "dependencies": { "fake-module": "latest" } } + +// @Filename: ../node_modules/fake-module/nested.ts +//// export var v1 = 5; +//// export function f1(); + +verify.importFixAtPosition([ +`import { f1 } from "fake-module/nested"; + +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportNodeModules2.ts b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules2.ts new file mode 100644 index 00000000000..ff48fbe182c --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules2.ts @@ -0,0 +1,25 @@ +/// + +//// [|f1/*0*/();|] + +// @Filename: ../package.json +//// { "dependencies": { "fake-module": "latest" } } + +// @Filename: ../node_modules/fake-module/notindex.d.ts +//// export var v1 = 5; +//// export function f1(); + +// @Filename: ../node_modules/fake-module/notindex.js +//// module.exports = { +//// v1: 5, +//// f1: function () {} +//// }; + +// @Filename: ../node_modules/fake-module/package.json +//// { "main":"./notindex.js", "typings":"./notindex.d.ts" } + +verify.importFixAtPosition([ +`import { f1 } from "fake-module"; + +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportNodeModules3.ts b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules3.ts new file mode 100644 index 00000000000..b1143cb4b41 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules3.ts @@ -0,0 +1,14 @@ +/// + +// @Filename: /a.ts +//// [|f1/*0*/();|] + +// @Filename: /node_modules/@types/random/index.d.ts +//// export var v1 = 5; +//// export function f1(); + +verify.importFixAtPosition([ +`import { f1 } from "random"; + +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportPaths0.ts b/tests/cases/fourslash/importNameCodeFixNewImportPaths0.ts new file mode 100644 index 00000000000..93cd6f92ef5 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportPaths0.ts @@ -0,0 +1,22 @@ +/// + +//// [|foo/*0*/();|] + +// @Filename: folder_a/f2.ts +//// export function foo() {}; + +// @Filename: tsconfig.json +//// { +//// "compilerOptions": { +//// "baseUrl": ".", +//// "paths": { +//// "a": [ "folder_a/f2" ] +//// } +//// } +//// } + +verify.importFixAtPosition([ +`import { foo } from "a"; + +foo();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportPaths1.ts b/tests/cases/fourslash/importNameCodeFixNewImportPaths1.ts new file mode 100644 index 00000000000..bb0f1e6705a --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportPaths1.ts @@ -0,0 +1,22 @@ +/// + +//// [|foo/*0*/();|] + +// @Filename: folder_b/f2.ts +//// export function foo() {}; + +// @Filename: tsconfig.json +//// { +//// "compilerOptions": { +//// "baseUrl": ".", +//// "paths": { +//// "b/*": [ "folder_b/*" ] +//// } +//// } +//// } + +verify.importFixAtPosition([ +`import { foo } from "b/f2"; + +foo();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportRootDirs0.ts b/tests/cases/fourslash/importNameCodeFixNewImportRootDirs0.ts new file mode 100644 index 00000000000..ae8ef03ccac --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportRootDirs0.ts @@ -0,0 +1,23 @@ +/// + +// @Filename: a/f1.ts +//// [|foo/*0*/();|] + +// @Filename: b/c/f2.ts +//// export function foo() {}; + +// @Filename: tsconfig.json +//// { +//// "compilerOptions": { +//// "rootDirs": [ +//// "a", +//// "b/c" +//// ] +//// } +//// } + +verify.importFixAtPosition([ +`import { foo } from "./f2"; + +foo();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportTypeRoots0.ts b/tests/cases/fourslash/importNameCodeFixNewImportTypeRoots0.ts new file mode 100644 index 00000000000..a6eb6a90759 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportTypeRoots0.ts @@ -0,0 +1,22 @@ +/// + +// @Filename: a/f1.ts +//// [|foo/*0*/();|] + +// @Filename: types/random/index.ts +//// export function foo() {}; + +// @Filename: tsconfig.json +//// { +//// "compilerOptions": { +//// "typeRoots": [ +//// "./types" +//// ] +//// } +//// } + +verify.importFixAtPosition([ +`import { foo } from "random"; + +foo();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixOptionalImport0.ts b/tests/cases/fourslash/importNameCodeFixOptionalImport0.ts new file mode 100644 index 00000000000..30b482a94d7 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixOptionalImport0.ts @@ -0,0 +1,20 @@ +/// + +// @Filename: a/f1.ts +//// [|import * as ns from "./foo"; +//// foo/*0*/();|] + +// @Filename: a/foo/bar.ts +//// export function foo() {}; + +// @Filename: a/foo.ts +//// export { foo } from "./foo/bar"; + +verify.importFixAtPosition([ +`import * as ns from "./foo"; +import { foo } from "./foo"; +foo();`, + +`import * as ns from "./foo"; +ns.foo();`, +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixOptionalImport1.ts b/tests/cases/fourslash/importNameCodeFixOptionalImport1.ts new file mode 100644 index 00000000000..343b0692260 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixOptionalImport1.ts @@ -0,0 +1,20 @@ +/// + +// @Filename: a/f1.ts +//// [|foo/*0*/();|] + +// @Filename: a/node_modules/bar/index.ts +//// export function foo() {}; + +// @Filename: a/foo.ts +//// export { foo } from "bar"; + +verify.importFixAtPosition([ +`import { foo } from "./foo"; + +foo();`, + +`import { foo } from "bar"; + +foo();`, +]); \ No newline at end of file diff --git a/tests/cases/fourslash/server/codefix.ts b/tests/cases/fourslash/server/codefix.ts index 7fbe2cb4fd7..9d0f42c9e4c 100644 --- a/tests/cases/fourslash/server/codefix.ts +++ b/tests/cases/fourslash/server/codefix.ts @@ -1,4 +1,4 @@ -/// +/// ////class Base{ ////}