1import { 2 __String, addRange, append, ArrayBindingElement, ArrayBindingOrAssignmentPattern, BindingElement, BindingName, 3 BindingOrAssignmentElement, BindingOrAssignmentElementTarget, BindingOrAssignmentPattern, Debug, 4 DestructuringAssignment, ElementAccessExpression, every, Expression, factory, forEach, 5 getElementsOfBindingOrAssignmentPattern, getInitializerOfBindingOrAssignmentElement, 6 getPropertyNameOfBindingOrAssignmentElement, getRestIndicatorOfBindingOrAssignmentElement, 7 getTargetOfBindingOrAssignmentElement, Identifier, idText, isArrayBindingElement, isArrayBindingOrAssignmentPattern, 8 isBindingElement, isBindingName, isBindingOrAssignmentPattern, isComputedPropertyName, isDeclarationBindingElement, 9 isDestructuringAssignment, isEmptyArrayLiteral, isEmptyObjectLiteral, isExpression, isIdentifier, 10 isLiteralExpression, isObjectBindingOrAssignmentPattern, isOmittedExpression, isPropertyNameLiteral, 11 isSimpleInlineableExpression, isStringOrNumericLiteralLike, isVariableDeclaration, last, LeftHandSideExpression, 12 map, Node, NodeFactory, nodeIsSynthesized, ObjectBindingOrAssignmentPattern, ParameterDeclaration, PropertyName, 13 setTextRange, some, TextRange, TransformationContext, TransformFlags, 14 tryGetPropertyNameOfBindingOrAssignmentElement, VariableDeclaration, visitNode, VisitResult, 15} from "../_namespaces/ts"; 16 17interface FlattenContext { 18 context: TransformationContext; 19 level: FlattenLevel; 20 downlevelIteration: boolean; 21 hoistTempVariables: boolean; 22 hasTransformedPriorElement?: boolean; // indicates whether we've transformed a prior declaration 23 emitExpression: (value: Expression) => void; 24 emitBindingOrAssignment: (target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node | undefined) => void; 25 createArrayBindingOrAssignmentPattern: (elements: BindingOrAssignmentElement[]) => ArrayBindingOrAssignmentPattern; 26 createObjectBindingOrAssignmentPattern: (elements: BindingOrAssignmentElement[]) => ObjectBindingOrAssignmentPattern; 27 createArrayBindingOrAssignmentElement: (node: Identifier) => BindingOrAssignmentElement; 28 visitor?: (node: Node) => VisitResult<Node>; 29} 30 31/** @internal */ 32export const enum FlattenLevel { 33 All, 34 ObjectRest, 35} 36 37/** 38 * Flattens a DestructuringAssignment or a VariableDeclaration to an expression. 39 * 40 * @param node The node to flatten. 41 * @param visitor An optional visitor used to visit initializers. 42 * @param context The transformation context. 43 * @param level Indicates the extent to which flattening should occur. 44 * @param needsValue An optional value indicating whether the value from the right-hand-side of 45 * the destructuring assignment is needed as part of a larger expression. 46 * @param createAssignmentCallback An optional callback used to create the assignment expression. 47 * 48 * @internal 49 */ 50export function flattenDestructuringAssignment( 51 node: VariableDeclaration | DestructuringAssignment, 52 visitor: ((node: Node) => VisitResult<Node>) | undefined, 53 context: TransformationContext, 54 level: FlattenLevel, 55 needsValue?: boolean, 56 createAssignmentCallback?: (name: Identifier, value: Expression, location?: TextRange) => Expression): Expression { 57 let location: TextRange = node; 58 let value: Expression | undefined; 59 if (isDestructuringAssignment(node)) { 60 value = node.right; 61 while (isEmptyArrayLiteral(node.left) || isEmptyObjectLiteral(node.left)) { 62 if (isDestructuringAssignment(value)) { 63 location = node = value; 64 value = node.right; 65 } 66 else { 67 return visitNode(value, visitor, isExpression); 68 } 69 } 70 } 71 72 let expressions: Expression[] | undefined; 73 const flattenContext: FlattenContext = { 74 context, 75 level, 76 downlevelIteration: !!context.getCompilerOptions().downlevelIteration, 77 hoistTempVariables: true, 78 emitExpression, 79 emitBindingOrAssignment, 80 createArrayBindingOrAssignmentPattern: elements => makeArrayAssignmentPattern(context.factory, elements), 81 createObjectBindingOrAssignmentPattern: elements => makeObjectAssignmentPattern(context.factory, elements), 82 createArrayBindingOrAssignmentElement: makeAssignmentElement, 83 visitor 84 }; 85 86 if (value) { 87 value = visitNode(value, visitor, isExpression); 88 89 if (isIdentifier(value) && bindingOrAssignmentElementAssignsToName(node, value.escapedText) || 90 bindingOrAssignmentElementContainsNonLiteralComputedName(node)) { 91 // If the right-hand value of the assignment is also an assignment target then 92 // we need to cache the right-hand value. 93 value = ensureIdentifier(flattenContext, value, /*reuseIdentifierExpressions*/ false, location); 94 } 95 else if (needsValue) { 96 // If the right-hand value of the destructuring assignment needs to be preserved (as 97 // is the case when the destructuring assignment is part of a larger expression), 98 // then we need to cache the right-hand value. 99 // 100 // The source map location for the assignment should point to the entire binary 101 // expression. 102 value = ensureIdentifier(flattenContext, value, /*reuseIdentifierExpressions*/ true, location); 103 } 104 else if (nodeIsSynthesized(node)) { 105 // Generally, the source map location for a destructuring assignment is the root 106 // expression. 107 // 108 // However, if the root expression is synthesized (as in the case 109 // of the initializer when transforming a ForOfStatement), then the source map 110 // location should point to the right-hand value of the expression. 111 location = value; 112 } 113 } 114 115 flattenBindingOrAssignmentElement(flattenContext, node, value, location, /*skipInitializer*/ isDestructuringAssignment(node)); 116 117 if (value && needsValue) { 118 if (!some(expressions)) { 119 return value; 120 } 121 122 expressions.push(value); 123 } 124 125 return context.factory.inlineExpressions(expressions!) || context.factory.createOmittedExpression(); 126 127 function emitExpression(expression: Expression) { 128 expressions = append(expressions, expression); 129 } 130 131 function emitBindingOrAssignment(target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node) { 132 Debug.assertNode(target, createAssignmentCallback ? isIdentifier : isExpression); 133 const expression = createAssignmentCallback 134 ? createAssignmentCallback(target as Identifier, value, location) 135 : setTextRange( 136 context.factory.createAssignment(visitNode(target as Expression, visitor, isExpression), value), 137 location 138 ); 139 expression.original = original; 140 emitExpression(expression); 141 } 142} 143 144function bindingOrAssignmentElementAssignsToName(element: BindingOrAssignmentElement, escapedName: __String): boolean { 145 const target = getTargetOfBindingOrAssignmentElement(element)!; // TODO: GH#18217 146 if (isBindingOrAssignmentPattern(target)) { 147 return bindingOrAssignmentPatternAssignsToName(target, escapedName); 148 } 149 else if (isIdentifier(target)) { 150 return target.escapedText === escapedName; 151 } 152 return false; 153} 154 155function bindingOrAssignmentPatternAssignsToName(pattern: BindingOrAssignmentPattern, escapedName: __String): boolean { 156 const elements = getElementsOfBindingOrAssignmentPattern(pattern); 157 for (const element of elements) { 158 if (bindingOrAssignmentElementAssignsToName(element, escapedName)) { 159 return true; 160 } 161 } 162 return false; 163} 164 165function bindingOrAssignmentElementContainsNonLiteralComputedName(element: BindingOrAssignmentElement): boolean { 166 const propertyName = tryGetPropertyNameOfBindingOrAssignmentElement(element); 167 if (propertyName && isComputedPropertyName(propertyName) && !isLiteralExpression(propertyName.expression)) { 168 return true; 169 } 170 const target = getTargetOfBindingOrAssignmentElement(element); 171 return !!target && isBindingOrAssignmentPattern(target) && bindingOrAssignmentPatternContainsNonLiteralComputedName(target); 172} 173 174function bindingOrAssignmentPatternContainsNonLiteralComputedName(pattern: BindingOrAssignmentPattern): boolean { 175 return !!forEach(getElementsOfBindingOrAssignmentPattern(pattern), bindingOrAssignmentElementContainsNonLiteralComputedName); 176} 177 178/** 179 * Flattens a VariableDeclaration or ParameterDeclaration to one or more variable declarations. 180 * 181 * @param node The node to flatten. 182 * @param visitor An optional visitor used to visit initializers. 183 * @param context The transformation context. 184 * @param boundValue The value bound to the declaration. 185 * @param skipInitializer A value indicating whether to ignore the initializer of `node`. 186 * @param hoistTempVariables Indicates whether temporary variables should not be recorded in-line. 187 * @param level Indicates the extent to which flattening should occur. 188 * 189 * @internal 190 */ 191export function flattenDestructuringBinding( 192 node: VariableDeclaration | ParameterDeclaration, 193 visitor: (node: Node) => VisitResult<Node>, 194 context: TransformationContext, 195 level: FlattenLevel, 196 rval?: Expression, 197 hoistTempVariables = false, 198 skipInitializer?: boolean): VariableDeclaration[] { 199 let pendingExpressions: Expression[] | undefined; 200 const pendingDeclarations: { pendingExpressions?: Expression[], name: BindingName, value: Expression, location?: TextRange, original?: Node; }[] = []; 201 const declarations: VariableDeclaration[] = []; 202 const flattenContext: FlattenContext = { 203 context, 204 level, 205 downlevelIteration: !!context.getCompilerOptions().downlevelIteration, 206 hoistTempVariables, 207 emitExpression, 208 emitBindingOrAssignment, 209 createArrayBindingOrAssignmentPattern: elements => makeArrayBindingPattern(context.factory, elements), 210 createObjectBindingOrAssignmentPattern: elements => makeObjectBindingPattern(context.factory, elements), 211 createArrayBindingOrAssignmentElement: name => makeBindingElement(context.factory, name), 212 visitor 213 }; 214 215 if (isVariableDeclaration(node)) { 216 let initializer = getInitializerOfBindingOrAssignmentElement(node); 217 if (initializer && (isIdentifier(initializer) && bindingOrAssignmentElementAssignsToName(node, initializer.escapedText) || 218 bindingOrAssignmentElementContainsNonLiteralComputedName(node))) { 219 // If the right-hand value of the assignment is also an assignment target then 220 // we need to cache the right-hand value. 221 initializer = ensureIdentifier(flattenContext, visitNode(initializer, flattenContext.visitor), /*reuseIdentifierExpressions*/ false, initializer); 222 node = context.factory.updateVariableDeclaration(node, node.name, /*exclamationToken*/ undefined, /*type*/ undefined, initializer); 223 } 224 } 225 226 flattenBindingOrAssignmentElement(flattenContext, node, rval, node, skipInitializer); 227 if (pendingExpressions) { 228 const temp = context.factory.createTempVariable(/*recordTempVariable*/ undefined); 229 if (hoistTempVariables) { 230 const value = context.factory.inlineExpressions(pendingExpressions); 231 pendingExpressions = undefined; 232 emitBindingOrAssignment(temp, value, /*location*/ undefined, /*original*/ undefined); 233 } 234 else { 235 context.hoistVariableDeclaration(temp); 236 const pendingDeclaration = last(pendingDeclarations); 237 pendingDeclaration.pendingExpressions = append( 238 pendingDeclaration.pendingExpressions, 239 context.factory.createAssignment(temp, pendingDeclaration.value) 240 ); 241 addRange(pendingDeclaration.pendingExpressions, pendingExpressions); 242 pendingDeclaration.value = temp; 243 } 244 } 245 for (const { pendingExpressions, name, value, location, original } of pendingDeclarations) { 246 const variable = context.factory.createVariableDeclaration( 247 name, 248 /*exclamationToken*/ undefined, 249 /*type*/ undefined, 250 pendingExpressions ? context.factory.inlineExpressions(append(pendingExpressions, value)) : value 251 ); 252 variable.original = original; 253 setTextRange(variable, location); 254 declarations.push(variable); 255 } 256 return declarations; 257 258 function emitExpression(value: Expression) { 259 pendingExpressions = append(pendingExpressions, value); 260 } 261 262 function emitBindingOrAssignment(target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange | undefined, original: Node | undefined) { 263 Debug.assertNode(target, isBindingName); 264 if (pendingExpressions) { 265 value = context.factory.inlineExpressions(append(pendingExpressions, value)); 266 pendingExpressions = undefined; 267 } 268 pendingDeclarations.push({ pendingExpressions, name: target, value, location, original }); 269 } 270} 271 272/** 273 * Flattens a BindingOrAssignmentElement into zero or more bindings or assignments. 274 * 275 * @param flattenContext Options used to control flattening. 276 * @param element The element to flatten. 277 * @param value The current RHS value to assign to the element. 278 * @param location The location to use for source maps and comments. 279 * @param skipInitializer An optional value indicating whether to include the initializer 280 * for the element. 281 */ 282function flattenBindingOrAssignmentElement( 283 flattenContext: FlattenContext, 284 element: BindingOrAssignmentElement, 285 value: Expression | undefined, 286 location: TextRange, 287 skipInitializer?: boolean) { 288 const bindingTarget = getTargetOfBindingOrAssignmentElement(element)!; // TODO: GH#18217 289 if (!skipInitializer) { 290 const initializer = visitNode(getInitializerOfBindingOrAssignmentElement(element), flattenContext.visitor, isExpression); 291 if (initializer) { 292 // Combine value and initializer 293 if (value) { 294 value = createDefaultValueCheck(flattenContext, value, initializer, location); 295 // If 'value' is not a simple expression, it could contain side-effecting code that should evaluate before an object or array binding pattern. 296 if (!isSimpleInlineableExpression(initializer) && isBindingOrAssignmentPattern(bindingTarget)) { 297 value = ensureIdentifier(flattenContext, value, /*reuseIdentifierExpressions*/ true, location); 298 } 299 } 300 else { 301 value = initializer; 302 } 303 } 304 else if (!value) { 305 // Use 'void 0' in absence of value and initializer 306 value = flattenContext.context.factory.createVoidZero(); 307 } 308 } 309 if (isObjectBindingOrAssignmentPattern(bindingTarget)) { 310 flattenObjectBindingOrAssignmentPattern(flattenContext, element, bindingTarget, value!, location); 311 } 312 else if (isArrayBindingOrAssignmentPattern(bindingTarget)) { 313 flattenArrayBindingOrAssignmentPattern(flattenContext, element, bindingTarget, value!, location); 314 } 315 else { 316 flattenContext.emitBindingOrAssignment(bindingTarget, value!, location, /*original*/ element); // TODO: GH#18217 317 } 318} 319 320/** 321 * Flattens an ObjectBindingOrAssignmentPattern into zero or more bindings or assignments. 322 * 323 * @param flattenContext Options used to control flattening. 324 * @param parent The parent element of the pattern. 325 * @param pattern The ObjectBindingOrAssignmentPattern to flatten. 326 * @param value The current RHS value to assign to the element. 327 * @param location The location to use for source maps and comments. 328 */ 329function flattenObjectBindingOrAssignmentPattern(flattenContext: FlattenContext, parent: BindingOrAssignmentElement, pattern: ObjectBindingOrAssignmentPattern, value: Expression, location: TextRange) { 330 const elements = getElementsOfBindingOrAssignmentPattern(pattern); 331 const numElements = elements.length; 332 if (numElements !== 1) { 333 // For anything other than a single-element destructuring we need to generate a temporary 334 // to ensure value is evaluated exactly once. Additionally, if we have zero elements 335 // we need to emit *something* to ensure that in case a 'var' keyword was already emitted, 336 // so in that case, we'll intentionally create that temporary. 337 const reuseIdentifierExpressions = !isDeclarationBindingElement(parent) || numElements !== 0; 338 value = ensureIdentifier(flattenContext, value, reuseIdentifierExpressions, location); 339 } 340 let bindingElements: BindingOrAssignmentElement[] | undefined; 341 let computedTempVariables: Expression[] | undefined; 342 for (let i = 0; i < numElements; i++) { 343 const element = elements[i]; 344 if (!getRestIndicatorOfBindingOrAssignmentElement(element)) { 345 const propertyName = getPropertyNameOfBindingOrAssignmentElement(element)!; 346 if (flattenContext.level >= FlattenLevel.ObjectRest 347 && !(element.transformFlags & (TransformFlags.ContainsRestOrSpread | TransformFlags.ContainsObjectRestOrSpread)) 348 && !(getTargetOfBindingOrAssignmentElement(element)!.transformFlags & (TransformFlags.ContainsRestOrSpread | TransformFlags.ContainsObjectRestOrSpread)) 349 && !isComputedPropertyName(propertyName)) { 350 bindingElements = append(bindingElements, visitNode(element, flattenContext.visitor)); 351 } 352 else { 353 if (bindingElements) { 354 flattenContext.emitBindingOrAssignment(flattenContext.createObjectBindingOrAssignmentPattern(bindingElements), value, location, pattern); 355 bindingElements = undefined; 356 } 357 const rhsValue = createDestructuringPropertyAccess(flattenContext, value, propertyName); 358 if (isComputedPropertyName(propertyName)) { 359 computedTempVariables = append<Expression>(computedTempVariables, (rhsValue as ElementAccessExpression).argumentExpression); 360 } 361 flattenBindingOrAssignmentElement(flattenContext, element, rhsValue, /*location*/ element); 362 } 363 } 364 else if (i === numElements - 1) { 365 if (bindingElements) { 366 flattenContext.emitBindingOrAssignment(flattenContext.createObjectBindingOrAssignmentPattern(bindingElements), value, location, pattern); 367 bindingElements = undefined; 368 } 369 const rhsValue = flattenContext.context.getEmitHelperFactory().createRestHelper(value, elements, computedTempVariables, pattern); 370 flattenBindingOrAssignmentElement(flattenContext, element, rhsValue, element); 371 } 372 } 373 if (bindingElements) { 374 flattenContext.emitBindingOrAssignment(flattenContext.createObjectBindingOrAssignmentPattern(bindingElements), value, location, pattern); 375 } 376} 377 378/** 379 * Flattens an ArrayBindingOrAssignmentPattern into zero or more bindings or assignments. 380 * 381 * @param flattenContext Options used to control flattening. 382 * @param parent The parent element of the pattern. 383 * @param pattern The ArrayBindingOrAssignmentPattern to flatten. 384 * @param value The current RHS value to assign to the element. 385 * @param location The location to use for source maps and comments. 386 */ 387function flattenArrayBindingOrAssignmentPattern(flattenContext: FlattenContext, parent: BindingOrAssignmentElement, pattern: ArrayBindingOrAssignmentPattern, value: Expression, location: TextRange) { 388 const elements = getElementsOfBindingOrAssignmentPattern(pattern); 389 const numElements = elements.length; 390 if (flattenContext.level < FlattenLevel.ObjectRest && flattenContext.downlevelIteration) { 391 // Read the elements of the iterable into an array 392 value = ensureIdentifier( 393 flattenContext, 394 setTextRange( 395 flattenContext.context.getEmitHelperFactory().createReadHelper( 396 value, 397 numElements > 0 && getRestIndicatorOfBindingOrAssignmentElement(elements[numElements - 1]) 398 ? undefined 399 : numElements 400 ), 401 location 402 ), 403 /*reuseIdentifierExpressions*/ false, 404 location 405 ); 406 } 407 else if (numElements !== 1 && (flattenContext.level < FlattenLevel.ObjectRest || numElements === 0) 408 || every(elements, isOmittedExpression)) { 409 // For anything other than a single-element destructuring we need to generate a temporary 410 // to ensure value is evaluated exactly once. Additionally, if we have zero elements 411 // we need to emit *something* to ensure that in case a 'var' keyword was already emitted, 412 // so in that case, we'll intentionally create that temporary. 413 // Or all the elements of the binding pattern are omitted expression such as "var [,] = [1,2]", 414 // then we will create temporary variable. 415 const reuseIdentifierExpressions = !isDeclarationBindingElement(parent) || numElements !== 0; 416 value = ensureIdentifier(flattenContext, value, reuseIdentifierExpressions, location); 417 } 418 let bindingElements: BindingOrAssignmentElement[] | undefined; 419 let restContainingElements: [Identifier, BindingOrAssignmentElement][] | undefined; 420 for (let i = 0; i < numElements; i++) { 421 const element = elements[i]; 422 if (flattenContext.level >= FlattenLevel.ObjectRest) { 423 // If an array pattern contains an ObjectRest, we must cache the result so that we 424 // can perform the ObjectRest destructuring in a different declaration 425 if (element.transformFlags & TransformFlags.ContainsObjectRestOrSpread || flattenContext.hasTransformedPriorElement && !isSimpleBindingOrAssignmentElement(element)) { 426 flattenContext.hasTransformedPriorElement = true; 427 const temp = flattenContext.context.factory.createTempVariable(/*recordTempVariable*/ undefined); 428 if (flattenContext.hoistTempVariables) { 429 flattenContext.context.hoistVariableDeclaration(temp); 430 } 431 432 restContainingElements = append(restContainingElements, [temp, element] as [Identifier, BindingOrAssignmentElement]); 433 bindingElements = append(bindingElements, flattenContext.createArrayBindingOrAssignmentElement(temp)); 434 } 435 else { 436 bindingElements = append(bindingElements, element); 437 } 438 } 439 else if (isOmittedExpression(element)) { 440 continue; 441 } 442 else if (!getRestIndicatorOfBindingOrAssignmentElement(element)) { 443 const rhsValue = flattenContext.context.factory.createElementAccessExpression(value, i); 444 flattenBindingOrAssignmentElement(flattenContext, element, rhsValue, /*location*/ element); 445 } 446 else if (i === numElements - 1) { 447 const rhsValue = flattenContext.context.factory.createArraySliceCall(value, i); 448 flattenBindingOrAssignmentElement(flattenContext, element, rhsValue, /*location*/ element); 449 } 450 } 451 if (bindingElements) { 452 flattenContext.emitBindingOrAssignment(flattenContext.createArrayBindingOrAssignmentPattern(bindingElements), value, location, pattern); 453 } 454 if (restContainingElements) { 455 for (const [id, element] of restContainingElements) { 456 flattenBindingOrAssignmentElement(flattenContext, element, id, element); 457 } 458 } 459} 460 461function isSimpleBindingOrAssignmentElement(element: BindingOrAssignmentElement): boolean { 462 const target = getTargetOfBindingOrAssignmentElement(element); 463 if (!target || isOmittedExpression(target)) return true; 464 const propertyName = tryGetPropertyNameOfBindingOrAssignmentElement(element); 465 if (propertyName && !isPropertyNameLiteral(propertyName)) return false; 466 const initializer = getInitializerOfBindingOrAssignmentElement(element); 467 if (initializer && !isSimpleInlineableExpression(initializer)) return false; 468 if (isBindingOrAssignmentPattern(target)) return every(getElementsOfBindingOrAssignmentPattern(target), isSimpleBindingOrAssignmentElement); 469 return isIdentifier(target); 470} 471 472/** 473 * Creates an expression used to provide a default value if a value is `undefined` at runtime. 474 * 475 * @param flattenContext Options used to control flattening. 476 * @param value The RHS value to test. 477 * @param defaultValue The default value to use if `value` is `undefined` at runtime. 478 * @param location The location to use for source maps and comments. 479 */ 480function createDefaultValueCheck(flattenContext: FlattenContext, value: Expression, defaultValue: Expression, location: TextRange): Expression { 481 value = ensureIdentifier(flattenContext, value, /*reuseIdentifierExpressions*/ true, location); 482 return flattenContext.context.factory.createConditionalExpression(flattenContext.context.factory.createTypeCheck(value, "undefined"), /*questionToken*/ undefined, defaultValue, /*colonToken*/ undefined, value); 483} 484 485/** 486 * Creates either a PropertyAccessExpression or an ElementAccessExpression for the 487 * right-hand side of a transformed destructuring assignment. 488 * 489 * @link https://tc39.github.io/ecma262/#sec-runtime-semantics-keyeddestructuringassignmentevaluation 490 * 491 * @param flattenContext Options used to control flattening. 492 * @param value The RHS value that is the source of the property. 493 * @param propertyName The destructuring property name. 494 */ 495function createDestructuringPropertyAccess(flattenContext: FlattenContext, value: Expression, propertyName: PropertyName): LeftHandSideExpression { 496 if (isComputedPropertyName(propertyName)) { 497 const argumentExpression = ensureIdentifier(flattenContext, visitNode(propertyName.expression, flattenContext.visitor), /*reuseIdentifierExpressions*/ false, /*location*/ propertyName); 498 return flattenContext.context.factory.createElementAccessExpression(value, argumentExpression); 499 } 500 else if (isStringOrNumericLiteralLike(propertyName)) { 501 const argumentExpression = factory.cloneNode(propertyName); 502 return flattenContext.context.factory.createElementAccessExpression(value, argumentExpression); 503 } 504 else { 505 const name = flattenContext.context.factory.createIdentifier(idText(propertyName)); 506 return flattenContext.context.factory.createPropertyAccessExpression(value, name); 507 } 508} 509 510/** 511 * Ensures that there exists a declared identifier whose value holds the given expression. 512 * This function is useful to ensure that the expression's value can be read from in subsequent expressions. 513 * Unless 'reuseIdentifierExpressions' is false, 'value' will be returned if it is just an identifier. 514 * 515 * @param flattenContext Options used to control flattening. 516 * @param value the expression whose value needs to be bound. 517 * @param reuseIdentifierExpressions true if identifier expressions can simply be returned; 518 * false if it is necessary to always emit an identifier. 519 * @param location The location to use for source maps and comments. 520 */ 521function ensureIdentifier(flattenContext: FlattenContext, value: Expression, reuseIdentifierExpressions: boolean, location: TextRange) { 522 if (isIdentifier(value) && reuseIdentifierExpressions) { 523 return value; 524 } 525 else { 526 const temp = flattenContext.context.factory.createTempVariable(/*recordTempVariable*/ undefined); 527 if (flattenContext.hoistTempVariables) { 528 flattenContext.context.hoistVariableDeclaration(temp); 529 flattenContext.emitExpression(setTextRange(flattenContext.context.factory.createAssignment(temp, value), location)); 530 } 531 else { 532 flattenContext.emitBindingOrAssignment(temp, value, location, /*original*/ undefined); 533 } 534 return temp; 535 } 536} 537 538function makeArrayBindingPattern(factory: NodeFactory, elements: BindingOrAssignmentElement[]) { 539 Debug.assertEachNode(elements, isArrayBindingElement); 540 return factory.createArrayBindingPattern(elements as ArrayBindingElement[]); 541} 542 543function makeArrayAssignmentPattern(factory: NodeFactory, elements: BindingOrAssignmentElement[]) { 544 return factory.createArrayLiteralExpression(map(elements, factory.converters.convertToArrayAssignmentElement)); 545} 546 547function makeObjectBindingPattern(factory: NodeFactory, elements: BindingOrAssignmentElement[]) { 548 Debug.assertEachNode(elements, isBindingElement); 549 return factory.createObjectBindingPattern(elements as BindingElement[]); 550} 551 552function makeObjectAssignmentPattern(factory: NodeFactory, elements: BindingOrAssignmentElement[]) { 553 return factory.createObjectLiteralExpression(map(elements, factory.converters.convertToObjectAssignmentElement)); 554} 555 556function makeBindingElement(factory: NodeFactory, name: Identifier) { 557 return factory.createBindingElement(/*dotDotDotToken*/ undefined, /*propertyName*/ undefined, name); 558} 559 560function makeAssignmentElement(name: Identifier) { 561 return name; 562} 563