• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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