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