source: frontend/node_modules/terser/lib/compress/tighten-body.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 13 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 59.4 KB
RevLine 
[9af201e]1/***********************************************************************
2
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS2
5
6 -------------------------------- (C) ---------------------------------
7
8 Author: Mihai Bazon
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
11
12 Distributed under the BSD license:
13
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
18 are met:
19
20 * Redistributions of source code must retain the above
21 copyright notice, this list of conditions and the following
22 disclaimer.
23
24 * Redistributions in binary form must reproduce the above
25 copyright notice, this list of conditions and the following
26 disclaimer in the documentation and/or other materials
27 provided with the distribution.
28
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 SUCH DAMAGE.
41
42 ***********************************************************************/
43
44import {
45 AST_Array,
46 AST_Arrow,
47 AST_Assign,
48 AST_Await,
49 AST_Binary,
50 AST_Block,
51 AST_BlockStatement,
52 AST_Break,
53 AST_Call,
54 AST_Case,
55 AST_Chain,
56 AST_Class,
57 AST_Conditional,
58 AST_Constant,
59 AST_Continue,
60 AST_Debugger,
61 AST_Default,
62 AST_Definitions,
63 AST_DefinitionsLike,
64 AST_Defun,
65 AST_Destructuring,
66 AST_Directive,
67 AST_Dot,
68 AST_DWLoop,
69 AST_EmptyStatement,
70 AST_Exit,
71 AST_Expansion,
72 AST_Export,
73 AST_For,
74 AST_ForIn,
75 AST_If,
76 AST_Import,
77 AST_IterationStatement,
78 AST_Lambda,
79 AST_LoopControl,
80 AST_Node,
81 AST_Number,
82 AST_Object,
83 AST_ObjectKeyVal,
84 AST_ObjectProperty,
85 AST_PropAccess,
86 AST_RegExp,
87 AST_Return,
88 AST_Scope,
89 AST_Sequence,
90 AST_SimpleStatement,
91 AST_Sub,
92 AST_Switch,
93 AST_Symbol,
94 AST_SymbolConst,
95 AST_SymbolDeclaration,
96 AST_SymbolDefun,
97 AST_SymbolFunarg,
98 AST_SymbolLambda,
99 AST_SymbolLet,
100 AST_SymbolRef,
101 AST_SymbolUsing,
102 AST_SymbolVar,
103 AST_This,
104 AST_Try,
105 AST_TryBlock,
106 AST_Unary,
107 AST_UnaryPostfix,
108 AST_UnaryPrefix,
109 AST_Using,
110 AST_Var,
111 AST_VarDef,
112 AST_With,
113 AST_Yield,
114
115 TreeTransformer,
116 TreeWalker,
117 walk,
118 walk_abort,
119
120 _NOINLINE,
121} from "../ast.js";
122import {
123 make_node,
124 make_void_0,
125 MAP,
126 member,
127 remove,
128 has_annotation
129} from "../utils/index.js";
130
131import { pure_prop_access_globals } from "./native-objects.js";
132import {
133 lazy_op,
134 unary_side_effects,
135 is_modified,
136 is_lhs,
137 aborts
138} from "./inference.js";
139import { WRITE_ONLY, clear_flag } from "./compressor-flags.js";
140import {
141 make_sequence,
142 merge_sequence,
143 maintain_this_binding,
144 is_func_expr,
145 is_identifier_atom,
146 is_ref_of,
147 can_be_evicted_from_block,
148 as_statement_array,
149} from "./common.js";
150
151function loop_body(x) {
152 if (x instanceof AST_IterationStatement) {
153 return x.body instanceof AST_BlockStatement ? x.body : x;
154 }
155 return x;
156}
157
158function is_lhs_read_only(lhs) {
159 if (lhs instanceof AST_This) return true;
160 if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda;
161 if (lhs instanceof AST_PropAccess) {
162 lhs = lhs.expression;
163 if (lhs instanceof AST_SymbolRef) {
164 if (lhs.is_immutable()) return false;
165 lhs = lhs.fixed_value();
166 }
167 if (!lhs) return true;
168 if (lhs instanceof AST_RegExp) return false;
169 if (lhs instanceof AST_Constant) return true;
170 return is_lhs_read_only(lhs);
171 }
172 return false;
173}
174
175/** var a = 1 --> var a*/
176function remove_initializers(var_statement) {
177 var decls = [];
178 var_statement.definitions.forEach(function(def) {
179 if (def.name instanceof AST_SymbolDeclaration) {
180 def.value = null;
181 decls.push(def);
182 } else {
183 def.declarations_as_names().forEach(name => {
184 decls.push(make_node(AST_VarDef, def, {
185 name,
186 value: null
187 }));
188 });
189 }
190 });
191 return decls.length ? make_node(AST_Var, var_statement, { definitions: decls }) : null;
192}
193
194/** Called on code which won't be executed but has an effect outside of itself: `var`, `function` statements, `export`, `import`. */
195export function extract_from_unreachable_code(compressor, stat, target) {
196 walk(stat, node => {
197 if (node instanceof AST_Var) {
198 const no_initializers = remove_initializers(node);
199 if (no_initializers) target.push(no_initializers);
200 return true;
201 }
202 if (
203 node instanceof AST_Defun
204 && (node === stat || !compressor.has_directive("use strict"))
205 ) {
206 target.push(node === stat ? node : make_node(AST_Var, node, {
207 definitions: [
208 make_node(AST_VarDef, node, {
209 name: make_node(AST_SymbolVar, node.name, node.name),
210 value: null
211 })
212 ]
213 }));
214 return true;
215 }
216 if (node instanceof AST_Export || node instanceof AST_Import) {
217 target.push(node);
218 return true;
219 }
220 if (node instanceof AST_Scope || node instanceof AST_Class) {
221 // Do not go into nested scopes
222 return true;
223 }
224 });
225}
226
227/** Tighten a bunch of statements together, and perform statement-level optimization. */
228export function tighten_body(statements, compressor) {
229 const nearest_scope = compressor.find_scope();
230 const defun_scope = nearest_scope.get_defun_scope();
231 const { in_loop, in_try } = find_loop_scope_try();
232
233 var CHANGED, max_iter = 10;
234 do {
235 CHANGED = false;
236 eliminate_spurious_blocks(statements);
237 if (compressor.option("dead_code")) {
238 eliminate_dead_code(statements, compressor);
239 }
240 if (compressor.option("if_return")) {
241 handle_if_return(statements, compressor);
242 }
243 if (compressor.sequences_limit > 0) {
244 sequencesize(statements, compressor);
245 sequencesize_2(statements, compressor);
246 }
247 if (compressor.option("join_vars")) {
248 join_consecutive_vars(statements);
249 }
250 if (compressor.option("collapse_vars")) {
251 collapse(statements, compressor);
252 }
253 } while (CHANGED && max_iter-- > 0);
254
255 function find_loop_scope_try() {
256 var node = compressor.self(), level = 0, in_loop = false, in_try = false;
257 do {
258 if (node instanceof AST_IterationStatement) {
259 in_loop = true;
260 } else if (node instanceof AST_Scope) {
261 break;
262 } else if (node instanceof AST_TryBlock) {
263 in_try = true;
264 }
265 } while (node = compressor.parent(level++));
266
267 return { in_loop, in_try };
268 }
269
270 // Search from right to left for assignment-like expressions:
271 // - `var a = x;`
272 // - `a = x;`
273 // - `++a`
274 // For each candidate, scan from left to right for first usage, then try
275 // to fold assignment into the site for compression.
276 // Will not attempt to collapse assignments into or past code blocks
277 // which are not sequentially executed, e.g. loops and conditionals.
278 function collapse(statements, compressor) {
279 if (nearest_scope.pinned() || defun_scope.pinned())
280 return statements;
281 var args;
282 var candidates = [];
283 var stat_index = statements.length;
284 var scanner = new TreeTransformer(function (node) {
285 if (abort)
286 return node;
287 // Skip nodes before `candidate` as quickly as possible
288 if (!hit) {
289 if (node !== hit_stack[hit_index])
290 return node;
291 hit_index++;
292 if (hit_index < hit_stack.length)
293 return handle_custom_scan_order(node);
294 hit = true;
295 stop_after = find_stop(node, 0);
296 if (stop_after === node)
297 abort = true;
298 return node;
299 }
300 // Stop immediately if these node types are encountered
301 var parent = scanner.parent();
302 if (node instanceof AST_Assign
303 && (node.logical || node.operator != "=" && lhs.equivalent_to(node.left))
304 || node instanceof AST_Await
305 || node instanceof AST_Using
306 || node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
307 ||
308 (node instanceof AST_Call || node instanceof AST_PropAccess)
309 && node.optional
310 || node instanceof AST_Debugger
311 || node instanceof AST_Destructuring
312 || node instanceof AST_Expansion
313 && node.expression instanceof AST_Symbol
314 && (
315 node.expression instanceof AST_This
316 || node.expression.definition().references.length > 1
317 )
318 || node instanceof AST_IterationStatement && !(node instanceof AST_For)
319 || node instanceof AST_LoopControl
320 || node instanceof AST_Try
321 || node instanceof AST_With
322 || node instanceof AST_Yield
323 || node instanceof AST_Export
324 || node instanceof AST_Class
325 || parent instanceof AST_For && node !== parent.init
326 || !replace_all
327 && (
328 node instanceof AST_SymbolRef
329 && !node.is_declared(compressor)
330 && !pure_prop_access_globals.has(node)
331 )
332 || node instanceof AST_SymbolRef
333 && parent instanceof AST_Call
334 && has_annotation(parent, _NOINLINE)
335 || node instanceof AST_ObjectProperty && node.key instanceof AST_Node
336 ) {
337 abort = true;
338 return node;
339 }
340 // Stop only if candidate is found within conditional branches
341 if (!stop_if_hit && (!lhs_local || !replace_all)
342 && (parent instanceof AST_Binary && lazy_op.has(parent.operator) && parent.left !== node
343 || parent instanceof AST_Conditional && parent.condition !== node
344 || parent instanceof AST_If && parent.condition !== node)) {
345 stop_if_hit = parent;
346 }
347 // Replace variable with assignment when found
348 if (
349 can_replace
350 && !(node instanceof AST_SymbolDeclaration)
351 && lhs.equivalent_to(node)
352 && !shadows(scanner.find_scope() || nearest_scope, lvalues)
353 ) {
354 if (stop_if_hit) {
355 abort = true;
356 return node;
357 }
358 if (is_lhs(node, parent)) {
359 if (value_def)
360 replaced++;
361 return node;
362 } else {
363 replaced++;
364 if (value_def && candidate instanceof AST_VarDef)
365 return node;
366 }
367 CHANGED = abort = true;
368 if (candidate instanceof AST_UnaryPostfix) {
369 return make_node(AST_UnaryPrefix, candidate, candidate);
370 }
371 if (candidate instanceof AST_VarDef) {
372 var def = candidate.name.definition();
373 var value = candidate.value;
374 if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
375 def.replaced++;
376 if (funarg && is_identifier_atom(value)) {
377 return value.transform(compressor);
378 } else {
379 return maintain_this_binding(parent, node, value);
380 }
381 }
382 return make_node(AST_Assign, candidate, {
383 operator: "=",
384 logical: false,
385 left: make_node(AST_SymbolRef, candidate.name, candidate.name),
386 right: value
387 });
388 }
389 clear_flag(candidate, WRITE_ONLY);
390 return candidate;
391 }
392 // These node types have child nodes that execute sequentially,
393 // but are otherwise not safe to scan into or beyond them.
394 var sym;
395 if (node instanceof AST_Call
396 || node instanceof AST_Exit
397 && (side_effects || lhs instanceof AST_PropAccess || may_modify(lhs))
398 || node instanceof AST_PropAccess
399 && (side_effects || node.expression.may_throw_on_access(compressor))
400 || node instanceof AST_SymbolRef
401 && ((lvalues.has(node.name) && lvalues.get(node.name).modified) || side_effects && may_modify(node))
402 || node instanceof AST_VarDef && node.value
403 && (lvalues.has(node.name.name) || side_effects && may_modify(node.name))
404 || node instanceof AST_Using
405 || (sym = is_lhs(node.left, node))
406 && (sym instanceof AST_PropAccess || lvalues.has(sym.name))
407 || may_throw
408 && (in_try ? node.has_side_effects(compressor) : side_effects_external(node))) {
409 stop_after = node;
410 if (node instanceof AST_Scope)
411 abort = true;
412 }
413 return handle_custom_scan_order(node);
414 }, function (node) {
415 if (abort)
416 return;
417 if (stop_after === node)
418 abort = true;
419 if (stop_if_hit === node)
420 stop_if_hit = null;
421 });
422
423 var multi_replacer = new TreeTransformer(function (node) {
424 if (abort)
425 return node;
426 // Skip nodes before `candidate` as quickly as possible
427 if (!hit) {
428 if (node !== hit_stack[hit_index])
429 return node;
430 hit_index++;
431 if (hit_index < hit_stack.length)
432 return;
433 hit = true;
434 return node;
435 }
436 // Replace variable when found
437 if (node instanceof AST_SymbolRef
438 && node.name == def.name) {
439 if (!--replaced)
440 abort = true;
441 if (is_lhs(node, multi_replacer.parent()))
442 return node;
443 def.replaced++;
444 value_def.replaced--;
445 return candidate.value;
446 }
447 // Skip (non-executed) functions and (leading) default case in switch statements
448 if (node instanceof AST_Default || node instanceof AST_Scope)
449 return node;
450 });
451
452 while (--stat_index >= 0) {
453 // Treat parameters as collapsible in IIFE, i.e.
454 // function(a, b){ ... }(x());
455 // would be translated into equivalent assignments:
456 // var a = x(), b = undefined;
457 if (stat_index == 0 && compressor.option("unused"))
458 extract_args();
459 // Find collapsible assignments
460 var hit_stack = [];
461 extract_candidates(statements[stat_index]);
462 while (candidates.length > 0) {
463 hit_stack = candidates.pop();
464 var hit_index = 0;
465 var candidate = hit_stack[hit_stack.length - 1];
466 var value_def = null;
467 var stop_after = null;
468 var stop_if_hit = null;
469 var lhs = get_lhs(candidate);
470 if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor))
471 continue;
472 // Locate symbols which may execute code outside of scanning range
473 var lvalues = get_lvalues(candidate);
474 var lhs_local = is_lhs_local(lhs);
475 if (lhs instanceof AST_SymbolRef) {
476 lvalues.set(lhs.name, { def: lhs.definition(), modified: false });
477 }
478 var side_effects = value_has_side_effects(candidate);
479 var replace_all = replace_all_symbols();
480 var may_throw = candidate.may_throw(compressor);
481 var funarg = candidate.name instanceof AST_SymbolFunarg;
482 var hit = funarg;
483 var abort = false, replaced = 0, can_replace = !args || !hit;
484 if (!can_replace) {
485 for (
486 let j = compressor.self().argnames.lastIndexOf(candidate.name) + 1;
487 !abort && j < args.length;
488 j++
489 ) {
490 args[j].transform(scanner);
491 }
492 can_replace = true;
493 }
494 for (var i = stat_index; !abort && i < statements.length; i++) {
495 statements[i].transform(scanner);
496 }
497 if (value_def) {
498 var def = candidate.name.definition();
499 if (abort && def.references.length - def.replaced > replaced)
500 replaced = false;
501 else {
502 abort = false;
503 hit_index = 0;
504 hit = funarg;
505 for (var i = stat_index; !abort && i < statements.length; i++) {
506 statements[i].transform(multi_replacer);
507 }
508 value_def.single_use = false;
509 }
510 }
511 if (replaced && !remove_candidate(candidate))
512 statements.splice(stat_index, 1);
513 }
514 }
515
516 function handle_custom_scan_order(node) {
517 // Skip (non-executed) functions
518 if (node instanceof AST_Scope)
519 return node;
520
521 // Scan case expressions first in a switch statement
522 if (node instanceof AST_Switch) {
523 node.expression = node.expression.transform(scanner);
524 for (var i = 0, len = node.body.length; !abort && i < len; i++) {
525 var branch = node.body[i];
526 if (branch instanceof AST_Case) {
527 if (!hit) {
528 if (branch !== hit_stack[hit_index])
529 continue;
530 hit_index++;
531 }
532 branch.expression = branch.expression.transform(scanner);
533 if (!replace_all)
534 break;
535 }
536 }
537 abort = true;
538 return node;
539 }
540 }
541
542 function redefined_within_scope(def, scope) {
543 if (def.global)
544 return false;
545 let cur_scope = def.scope;
546 while (cur_scope && cur_scope !== scope) {
547 if (cur_scope.variables.has(def.name)) {
548 return true;
549 }
550 cur_scope = cur_scope.parent_scope;
551 }
552 return false;
553 }
554
555 function has_overlapping_symbol(fn, arg, fn_strict) {
556 var found = false, scan_this = !(fn instanceof AST_Arrow);
557 arg.walk(new TreeWalker(function (node, descend) {
558 if (found)
559 return true;
560 if (node instanceof AST_SymbolRef && (fn.variables.has(node.name) || redefined_within_scope(node.definition(), fn))) {
561 var s = node.definition().scope;
562 if (s !== defun_scope)
563 while (s = s.parent_scope) {
564 if (s === defun_scope)
565 return true;
566 }
567 return found = true;
568 }
569 if ((fn_strict || scan_this) && node instanceof AST_This) {
570 return found = true;
571 }
572 if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
573 var prev = scan_this;
574 scan_this = false;
575 descend();
576 scan_this = prev;
577 return true;
578 }
579 }));
580 return found;
581 }
582
583 function arg_is_injectable(arg) {
584 if (arg instanceof AST_Expansion) return false;
585 const contains_await = walk(arg, (node) => {
586 if (node instanceof AST_Await) return walk_abort;
587 });
588 if (contains_await) return false;
589 return true;
590 }
591 function extract_args() {
592 var iife, fn = compressor.self();
593 if (is_func_expr(fn)
594 && !fn.name
595 && !fn.uses_arguments
596 && !fn.pinned()
597 && (iife = compressor.parent()) instanceof AST_Call
598 && iife.expression === fn
599 && iife.args.every(arg_is_injectable)
600 ) {
601 var fn_strict = compressor.has_directive("use strict");
602 if (fn_strict && !member(fn_strict, fn.body))
603 fn_strict = false;
604 var len = fn.argnames.length;
605 args = iife.args.slice(len);
606 var names = new Set();
607 for (var i = len; --i >= 0;) {
608 var sym = fn.argnames[i];
609 var arg = iife.args[i];
610 // The following two line fix is a duplicate of the fix at
611 // https://github.com/terser/terser/commit/011d3eb08cefe6922c7d1bdfa113fc4aeaca1b75
612 // This might mean that these two pieces of code (one here in collapse_vars and another in reduce_vars
613 // Might be doing the exact same thing.
614 const def = sym.definition && sym.definition();
615 const is_reassigned = def && def.orig.length > 1;
616 if (is_reassigned)
617 continue;
618 args.unshift(make_node(AST_VarDef, sym, {
619 name: sym,
620 value: arg
621 }));
622 if (names.has(sym.name))
623 continue;
624 names.add(sym.name);
625 if (sym instanceof AST_Expansion) {
626 var elements = iife.args.slice(i);
627 if (elements.every((arg) => !has_overlapping_symbol(fn, arg, fn_strict)
628 )) {
629 candidates.unshift([make_node(AST_VarDef, sym, {
630 name: sym.expression,
631 value: make_node(AST_Array, iife, {
632 elements: elements
633 })
634 })]);
635 }
636 } else {
637 if (!arg) {
638 arg = make_void_0(sym).transform(compressor);
639 } else if (arg instanceof AST_Lambda && arg.pinned()
640 || has_overlapping_symbol(fn, arg, fn_strict)) {
641 arg = null;
642 }
643 if (arg)
644 candidates.unshift([make_node(AST_VarDef, sym, {
645 name: sym,
646 value: arg
647 })]);
648 }
649 }
650 }
651 }
652
653 function extract_candidates(expr) {
654 hit_stack.push(expr);
655 if (expr instanceof AST_Assign) {
656 if (!expr.left.has_side_effects(compressor)
657 && !(expr.right instanceof AST_Chain)) {
658 candidates.push(hit_stack.slice());
659 }
660 extract_candidates(expr.right);
661 } else if (expr instanceof AST_Binary) {
662 extract_candidates(expr.left);
663 extract_candidates(expr.right);
664 } else if (expr instanceof AST_Call && !has_annotation(expr, _NOINLINE)) {
665 extract_candidates(expr.expression);
666 expr.args.forEach(extract_candidates);
667 } else if (expr instanceof AST_Case) {
668 extract_candidates(expr.expression);
669 } else if (expr instanceof AST_Conditional) {
670 extract_candidates(expr.condition);
671 extract_candidates(expr.consequent);
672 extract_candidates(expr.alternative);
673 } else if (expr instanceof AST_Definitions) {
674 var len = expr.definitions.length;
675 // limit number of trailing variable definitions for consideration
676 var i = len - 200;
677 if (i < 0)
678 i = 0;
679 for (; i < len; i++) {
680 extract_candidates(expr.definitions[i]);
681 }
682 } else if (expr instanceof AST_DWLoop) {
683 extract_candidates(expr.condition);
684 if (!(expr.body instanceof AST_Block)) {
685 extract_candidates(expr.body);
686 }
687 } else if (expr instanceof AST_Exit) {
688 if (expr.value)
689 extract_candidates(expr.value);
690 } else if (expr instanceof AST_For) {
691 if (expr.init)
692 extract_candidates(expr.init);
693 if (expr.condition)
694 extract_candidates(expr.condition);
695 if (expr.step)
696 extract_candidates(expr.step);
697 if (!(expr.body instanceof AST_Block)) {
698 extract_candidates(expr.body);
699 }
700 } else if (expr instanceof AST_ForIn) {
701 extract_candidates(expr.object);
702 if (!(expr.body instanceof AST_Block)) {
703 extract_candidates(expr.body);
704 }
705 } else if (expr instanceof AST_If) {
706 extract_candidates(expr.condition);
707 if (!(expr.body instanceof AST_Block)) {
708 extract_candidates(expr.body);
709 }
710 if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
711 extract_candidates(expr.alternative);
712 }
713 } else if (expr instanceof AST_Sequence) {
714 expr.expressions.forEach(extract_candidates);
715 } else if (expr instanceof AST_SimpleStatement) {
716 extract_candidates(expr.body);
717 } else if (expr instanceof AST_Switch) {
718 extract_candidates(expr.expression);
719 expr.body.forEach(extract_candidates);
720 } else if (expr instanceof AST_Unary) {
721 if (expr.operator == "++" || expr.operator == "--") {
722 candidates.push(hit_stack.slice());
723 }
724 } else if (expr instanceof AST_VarDef) {
725 if (expr.value && !(expr.value instanceof AST_Chain)) {
726 candidates.push(hit_stack.slice());
727 extract_candidates(expr.value);
728 }
729 }
730 hit_stack.pop();
731 }
732
733 function find_stop(node, level, write_only) {
734 var parent = scanner.parent(level);
735 if (parent instanceof AST_Assign) {
736 if (write_only
737 && !parent.logical
738 && !(parent.left instanceof AST_PropAccess
739 || lvalues.has(parent.left.name))) {
740 return find_stop(parent, level + 1, write_only);
741 }
742 return node;
743 }
744 if (parent instanceof AST_Binary) {
745 if (write_only && (!lazy_op.has(parent.operator) || parent.left === node)) {
746 return find_stop(parent, level + 1, write_only);
747 }
748 return node;
749 }
750 if (parent instanceof AST_Call)
751 return node;
752 if (parent instanceof AST_Case)
753 return node;
754 if (parent instanceof AST_Conditional) {
755 if (write_only && parent.condition === node) {
756 return find_stop(parent, level + 1, write_only);
757 }
758 return node;
759 }
760 if (parent instanceof AST_Definitions) {
761 return find_stop(parent, level + 1, true);
762 }
763 if (parent instanceof AST_Exit) {
764 return write_only ? find_stop(parent, level + 1, write_only) : node;
765 }
766 if (parent instanceof AST_If) {
767 if (write_only && parent.condition === node) {
768 return find_stop(parent, level + 1, write_only);
769 }
770 return node;
771 }
772 if (parent instanceof AST_IterationStatement)
773 return node;
774 if (parent instanceof AST_Sequence) {
775 return find_stop(parent, level + 1, parent.tail_node() !== node);
776 }
777 if (parent instanceof AST_SimpleStatement) {
778 return find_stop(parent, level + 1, true);
779 }
780 if (parent instanceof AST_Switch)
781 return node;
782 if (parent instanceof AST_VarDef)
783 return node;
784 return null;
785 }
786
787 function mangleable_var(var_def) {
788 var value = var_def.value;
789 if (!(value instanceof AST_SymbolRef))
790 return;
791 if (value.name == "arguments")
792 return;
793 var def = value.definition();
794 if (def.undeclared)
795 return;
796 return value_def = def;
797 }
798
799 function get_lhs(expr) {
800 if (expr instanceof AST_Assign && expr.logical) {
801 return false;
802 } else if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
803 var def = expr.name.definition();
804 if (!member(expr.name, def.orig))
805 return;
806 var referenced = def.references.length - def.replaced;
807 if (!referenced)
808 return;
809 var declared = def.orig.length - def.eliminated;
810 if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
811 || (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
812 return make_node(AST_SymbolRef, expr.name, expr.name);
813 }
814 } else {
815 const lhs = expr instanceof AST_Assign
816 ? expr.left
817 : expr.expression;
818 return !is_ref_of(lhs, AST_SymbolConst)
819 && !is_ref_of(lhs, AST_SymbolLet)
820 && !is_ref_of(lhs, AST_SymbolUsing)
821 && lhs;
822 }
823 }
824
825 function get_rvalue(expr) {
826 if (expr instanceof AST_Assign) {
827 return expr.right;
828 } else {
829 return expr.value;
830 }
831 }
832
833 function get_lvalues(expr) {
834 var lvalues = new Map();
835 if (expr instanceof AST_Unary)
836 return lvalues;
837 var tw = new TreeWalker(function (node) {
838 var sym = node;
839 while (sym instanceof AST_PropAccess)
840 sym = sym.expression;
841 if (sym instanceof AST_SymbolRef) {
842 const prev = lvalues.get(sym.name);
843 if (!prev || !prev.modified) {
844 lvalues.set(sym.name, {
845 def: sym.definition(),
846 modified: is_modified(compressor, tw, node, node, 0)
847 });
848 }
849 }
850 });
851 get_rvalue(expr).walk(tw);
852 return lvalues;
853 }
854
855 function remove_candidate(expr) {
856 if (expr.name instanceof AST_SymbolFunarg) {
857 var iife = compressor.parent(), argnames = compressor.self().argnames;
858 var index = argnames.indexOf(expr.name);
859 if (index < 0) {
860 iife.args.length = Math.min(iife.args.length, argnames.length - 1);
861 } else {
862 var args = iife.args;
863 if (args[index])
864 args[index] = make_node(AST_Number, args[index], {
865 value: 0
866 });
867 }
868 return true;
869 }
870 var found = false;
871 return statements[stat_index].transform(new TreeTransformer(function (node, descend, in_list) {
872 if (found)
873 return node;
874 if (node === expr || node.body === expr) {
875 found = true;
876 if (node instanceof AST_VarDef) {
877 node.value = node.name instanceof AST_SymbolConst
878 ? make_void_0(node.value) // `const` always needs value.
879 : null;
880 return node;
881 }
882 return in_list ? MAP.skip : null;
883 }
884 }, function (node) {
885 if (node instanceof AST_Sequence)
886 switch (node.expressions.length) {
887 case 0: return null;
888 case 1: return node.expressions[0];
889 }
890 }));
891 }
892
893 function is_lhs_local(lhs) {
894 while (lhs instanceof AST_PropAccess)
895 lhs = lhs.expression;
896 return lhs instanceof AST_SymbolRef
897 && lhs.definition().scope.get_defun_scope() === defun_scope
898 && !(in_loop
899 && (lvalues.has(lhs.name)
900 || candidate instanceof AST_Unary
901 || (candidate instanceof AST_Assign
902 && !candidate.logical
903 && candidate.operator != "=")));
904 }
905
906 function value_has_side_effects(expr) {
907 if (expr instanceof AST_Unary)
908 return unary_side_effects.has(expr.operator);
909 return get_rvalue(expr).has_side_effects(compressor);
910 }
911
912 function replace_all_symbols() {
913 if (side_effects)
914 return false;
915 if (value_def)
916 return true;
917 if (lhs instanceof AST_SymbolRef) {
918 var def = lhs.definition();
919 if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
920 return true;
921 }
922 }
923 return false;
924 }
925
926 function may_modify(sym) {
927 if (!sym.definition)
928 return true; // AST_Destructuring
929 var def = sym.definition();
930 if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun)
931 return false;
932 if (def.scope.get_defun_scope() !== defun_scope)
933 return true;
934 return def.references.some((ref) =>
935 ref.scope.get_defun_scope() !== defun_scope
936 );
937 }
938
939 function side_effects_external(node, lhs) {
940 if (node instanceof AST_Assign)
941 return side_effects_external(node.left, true);
942 if (node instanceof AST_Unary)
943 return side_effects_external(node.expression, true);
944 if (node instanceof AST_VarDef)
945 return node.value && side_effects_external(node.value);
946 if (lhs) {
947 if (node instanceof AST_Dot)
948 return side_effects_external(node.expression, true);
949 if (node instanceof AST_Sub)
950 return side_effects_external(node.expression, true);
951 if (node instanceof AST_SymbolRef)
952 return node.definition().scope.get_defun_scope() !== defun_scope;
953 }
954 return false;
955 }
956
957 /**
958 * Will any of the pulled-in lvalues shadow a variable in newScope or parents?
959 * similar to scope_encloses_variables_in_this_scope */
960 function shadows(my_scope, lvalues) {
961 for (const { def } of lvalues.values()) {
962 const looked_up = my_scope.find_variable(def.name);
963 if (looked_up) {
964 if (looked_up === def) continue;
965 return true;
966 }
967 }
968 return false;
969 }
970 }
971
972 function eliminate_spurious_blocks(statements) {
973 var seen_dirs = [];
974 for (var i = 0; i < statements.length;) {
975 var stat = statements[i];
976 if (stat instanceof AST_BlockStatement && stat.body.every(can_be_evicted_from_block)) {
977 CHANGED = true;
978 eliminate_spurious_blocks(stat.body);
979 statements.splice(i, 1, ...stat.body);
980 i += stat.body.length;
981 } else if (stat instanceof AST_EmptyStatement) {
982 CHANGED = true;
983 statements.splice(i, 1);
984 } else if (stat instanceof AST_Directive) {
985 if (seen_dirs.indexOf(stat.value) < 0) {
986 i++;
987 seen_dirs.push(stat.value);
988 } else {
989 CHANGED = true;
990 statements.splice(i, 1);
991 }
992 } else
993 i++;
994 }
995 }
996
997 function handle_if_return(statements, compressor) {
998 var self = compressor.self();
999 var multiple_if_returns = has_multiple_if_returns(statements);
1000 var in_lambda = self instanceof AST_Lambda;
1001 // Prevent extremely deep nesting
1002 // https://github.com/terser/terser/issues/1432
1003 // https://github.com/webpack/webpack/issues/17548
1004 const iteration_start = Math.min(statements.length, 500);
1005 for (var i = iteration_start; --i >= 0;) {
1006 var stat = statements[i];
1007 var j = next_index(i);
1008 var next = statements[j];
1009
1010 if (in_lambda && !next && stat instanceof AST_Return) {
1011 if (!stat.value) {
1012 CHANGED = true;
1013 statements.splice(i, 1);
1014 continue;
1015 }
1016 if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
1017 CHANGED = true;
1018 statements[i] = make_node(AST_SimpleStatement, stat, {
1019 body: stat.value.expression
1020 });
1021 continue;
1022 }
1023 }
1024
1025 if (stat instanceof AST_If) {
1026 let ab, new_else;
1027
1028 ab = aborts(stat.body);
1029 if (
1030 can_merge_flow(ab)
1031 && (new_else = as_statement_array_with_return(stat.body, ab))
1032 ) {
1033 if (ab.label) {
1034 remove(ab.label.thedef.references, ab);
1035 }
1036 CHANGED = true;
1037 stat = stat.clone();
1038 stat.condition = stat.condition.negate(compressor);
1039 stat.body = make_node(AST_BlockStatement, stat, {
1040 body: as_statement_array(stat.alternative).concat(extract_defuns())
1041 });
1042 stat.alternative = make_node(AST_BlockStatement, stat, {
1043 body: new_else
1044 });
1045 statements[i] = stat.transform(compressor);
1046 continue;
1047 }
1048
1049 ab = aborts(stat.alternative);
1050 if (
1051 can_merge_flow(ab)
1052 && (new_else = as_statement_array_with_return(stat.alternative, ab))
1053 ) {
1054 if (ab.label) {
1055 remove(ab.label.thedef.references, ab);
1056 }
1057 CHANGED = true;
1058 stat = stat.clone();
1059 stat.body = make_node(AST_BlockStatement, stat.body, {
1060 body: as_statement_array(stat.body).concat(extract_defuns())
1061 });
1062 stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
1063 body: new_else
1064 });
1065 statements[i] = stat.transform(compressor);
1066 continue;
1067 }
1068 }
1069
1070 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
1071 var value = stat.body.value;
1072 //---
1073 // pretty silly case, but:
1074 // if (foo()) return; return; ==> foo(); return;
1075 if (!value && !stat.alternative
1076 && (in_lambda && !next || next instanceof AST_Return && !next.value)) {
1077 CHANGED = true;
1078 statements[i] = make_node(AST_SimpleStatement, stat.condition, {
1079 body: stat.condition
1080 });
1081 continue;
1082 }
1083 //---
1084 // if (foo()) return x; return y; ==> return foo() ? x : y;
1085 if (value && !stat.alternative && next instanceof AST_Return && next.value) {
1086 CHANGED = true;
1087 stat = stat.clone();
1088 stat.alternative = next;
1089 statements[i] = stat.transform(compressor);
1090 statements.splice(j, 1);
1091 continue;
1092 }
1093 //---
1094 // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
1095 if (value && !stat.alternative
1096 && (!next && in_lambda && multiple_if_returns
1097 || next instanceof AST_Return)) {
1098 CHANGED = true;
1099 stat = stat.clone();
1100 stat.alternative = next || make_node(AST_Return, stat, {
1101 value: null
1102 });
1103 statements[i] = stat.transform(compressor);
1104 if (next)
1105 statements.splice(j, 1);
1106 continue;
1107 }
1108 //---
1109 // if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
1110 //
1111 // if sequences is not enabled, this can lead to an endless loop (issue #866).
1112 // however, with sequences on this helps producing slightly better output for
1113 // the example code.
1114 var prev = statements[prev_index(i)];
1115 if (compressor.option("sequences") && in_lambda && !stat.alternative
1116 && prev instanceof AST_If && prev.body instanceof AST_Return
1117 && next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
1118 CHANGED = true;
1119 stat = stat.clone();
1120 stat.alternative = make_node(AST_BlockStatement, next, {
1121 body: [
1122 next,
1123 make_node(AST_Return, next, {
1124 value: null
1125 })
1126 ]
1127 });
1128 statements[i] = stat.transform(compressor);
1129 statements.splice(j, 1);
1130 continue;
1131 }
1132 }
1133 }
1134
1135 function has_multiple_if_returns(statements) {
1136 var n = 0;
1137 for (var i = statements.length; --i >= 0;) {
1138 var stat = statements[i];
1139 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
1140 if (++n > 1)
1141 return true;
1142 }
1143 }
1144 return false;
1145 }
1146
1147 function is_return_void(value) {
1148 return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
1149 }
1150
1151 function can_merge_flow(ab) {
1152 if (!ab)
1153 return false;
1154 for (var j = i + 1, len = statements.length; j < len; j++) {
1155 var stat = statements[j];
1156 if (stat instanceof AST_DefinitionsLike && !(stat instanceof AST_Var))
1157 return false;
1158 }
1159 var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
1160 return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
1161 || ab instanceof AST_Continue && self === loop_body(lct)
1162 || ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
1163 }
1164
1165 function extract_defuns() {
1166 var tail = statements.slice(i + 1);
1167 statements.length = i + 1;
1168 return tail.filter(function (stat) {
1169 if (stat instanceof AST_Defun) {
1170 statements.push(stat);
1171 return false;
1172 }
1173 return true;
1174 });
1175 }
1176
1177 function as_statement_array_with_return(node, ab) {
1178 var body = as_statement_array(node);
1179 if (ab !== body[body.length - 1]) {
1180 return undefined;
1181 }
1182 body = body.slice(0, -1);
1183 if (!body.every(stat => can_be_evicted_from_block(stat))) {
1184 return undefined;
1185 }
1186 if (ab.value) {
1187 body.push(make_node(AST_SimpleStatement, ab.value, {
1188 body: ab.value.expression
1189 }));
1190 }
1191 return body;
1192 }
1193
1194 function next_index(i) {
1195 for (var j = i + 1, len = statements.length; j < len; j++) {
1196 var stat = statements[j];
1197 if (!(stat instanceof AST_Var && declarations_only(stat))) {
1198 break;
1199 }
1200 }
1201 return j;
1202 }
1203
1204 function prev_index(i) {
1205 for (var j = i; --j >= 0;) {
1206 var stat = statements[j];
1207 if (!(stat instanceof AST_Var && declarations_only(stat))) {
1208 break;
1209 }
1210 }
1211 return j;
1212 }
1213 }
1214
1215 function eliminate_dead_code(statements, compressor) {
1216 var has_quit;
1217 var self = compressor.self();
1218 for (var i = 0, n = 0, len = statements.length; i < len; i++) {
1219 var stat = statements[i];
1220 if (stat instanceof AST_LoopControl) {
1221 var lct = compressor.loopcontrol_target(stat);
1222 if (stat instanceof AST_Break
1223 && !(lct instanceof AST_IterationStatement)
1224 && loop_body(lct) === self
1225 || stat instanceof AST_Continue
1226 && loop_body(lct) === self) {
1227 if (stat.label) {
1228 remove(stat.label.thedef.references, stat);
1229 }
1230 } else {
1231 statements[n++] = stat;
1232 }
1233 } else {
1234 statements[n++] = stat;
1235 }
1236 if (aborts(stat)) {
1237 has_quit = statements.slice(i + 1);
1238 break;
1239 }
1240 }
1241 statements.length = n;
1242 CHANGED = n != len;
1243 if (has_quit)
1244 has_quit.forEach(function (stat) {
1245 extract_from_unreachable_code(compressor, stat, statements);
1246 });
1247 }
1248
1249 function declarations_only(node) {
1250 return node.definitions.every((var_def) => !var_def.value);
1251 }
1252
1253 function sequencesize(statements, compressor) {
1254 if (statements.length < 2)
1255 return;
1256 var seq = [], n = 0;
1257 function push_seq() {
1258 if (!seq.length)
1259 return;
1260 var body = make_sequence(seq[0], seq);
1261 statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
1262 seq = [];
1263 }
1264 for (var i = 0, len = statements.length; i < len; i++) {
1265 var stat = statements[i];
1266 if (stat instanceof AST_SimpleStatement) {
1267 if (seq.length >= compressor.sequences_limit)
1268 push_seq();
1269 var body = stat.body;
1270 if (seq.length > 0)
1271 body = body.drop_side_effect_free(compressor);
1272 if (body)
1273 merge_sequence(seq, body);
1274 } else if (stat instanceof AST_Definitions && declarations_only(stat)
1275 || stat instanceof AST_Defun) {
1276 statements[n++] = stat;
1277 } else {
1278 push_seq();
1279 statements[n++] = stat;
1280 }
1281 }
1282 push_seq();
1283 statements.length = n;
1284 if (n != len)
1285 CHANGED = true;
1286 }
1287
1288 function to_simple_statement(block, decls) {
1289 if (!(block instanceof AST_BlockStatement))
1290 return block;
1291 var stat = null;
1292 for (var i = 0, len = block.body.length; i < len; i++) {
1293 var line = block.body[i];
1294 if (line instanceof AST_Var && declarations_only(line)) {
1295 decls.push(line);
1296 } else if (stat || line instanceof AST_DefinitionsLike && !(line instanceof AST_Var)) {
1297 return false;
1298 } else {
1299 stat = line;
1300 }
1301 }
1302 return stat;
1303 }
1304
1305 function sequencesize_2(statements, compressor) {
1306 function cons_seq(right) {
1307 n--;
1308 CHANGED = true;
1309 var left = prev.body;
1310 return make_sequence(left, [left, right]).transform(compressor);
1311 }
1312 var n = 0, prev;
1313 for (var i = 0; i < statements.length; i++) {
1314 var stat = statements[i];
1315 if (prev) {
1316 if (stat instanceof AST_Exit) {
1317 stat.value = cons_seq(stat.value || make_void_0(stat).transform(compressor));
1318 } else if (stat instanceof AST_For) {
1319 if (!(stat.init instanceof AST_DefinitionsLike)) {
1320 const abort = walk(prev.body, node => {
1321 if (node instanceof AST_Scope)
1322 return true;
1323 if (node instanceof AST_Binary
1324 && node.operator === "in") {
1325 return walk_abort;
1326 }
1327 });
1328 if (!abort) {
1329 if (stat.init)
1330 stat.init = cons_seq(stat.init);
1331 else {
1332 stat.init = prev.body;
1333 n--;
1334 CHANGED = true;
1335 }
1336 }
1337 }
1338 } else if (stat instanceof AST_ForIn) {
1339 if (!(stat.init instanceof AST_DefinitionsLike) || stat.init instanceof AST_Var) {
1340 stat.object = cons_seq(stat.object);
1341 }
1342 } else if (stat instanceof AST_If) {
1343 stat.condition = cons_seq(stat.condition);
1344 } else if (stat instanceof AST_Switch) {
1345 stat.expression = cons_seq(stat.expression);
1346 } else if (stat instanceof AST_With) {
1347 stat.expression = cons_seq(stat.expression);
1348 }
1349 }
1350 if (compressor.option("conditionals") && stat instanceof AST_If) {
1351 var decls = [];
1352 var body = to_simple_statement(stat.body, decls);
1353 var alt = to_simple_statement(stat.alternative, decls);
1354 if (body !== false && alt !== false && decls.length > 0) {
1355 var len = decls.length;
1356 decls.push(make_node(AST_If, stat, {
1357 condition: stat.condition,
1358 body: body || make_node(AST_EmptyStatement, stat.body),
1359 alternative: alt
1360 }));
1361 decls.unshift(n, 1);
1362 [].splice.apply(statements, decls);
1363 i += len;
1364 n += len + 1;
1365 prev = null;
1366 CHANGED = true;
1367 continue;
1368 }
1369 }
1370 statements[n++] = stat;
1371 prev = stat instanceof AST_SimpleStatement ? stat : null;
1372 }
1373 statements.length = n;
1374 }
1375
1376 function join_object_assignments(defn, body) {
1377 if (!(defn instanceof AST_Definitions))
1378 return;
1379 var def = defn.definitions[defn.definitions.length - 1];
1380 if (!(def.value instanceof AST_Object))
1381 return;
1382 var exprs;
1383 if (body instanceof AST_Assign && !body.logical) {
1384 exprs = [body];
1385 } else if (body instanceof AST_Sequence) {
1386 exprs = body.expressions.slice();
1387 }
1388 if (!exprs)
1389 return;
1390 var trimmed = false;
1391 do {
1392 var node = exprs[0];
1393 if (!(node instanceof AST_Assign))
1394 break;
1395 if (node.operator != "=")
1396 break;
1397 if (!(node.left instanceof AST_PropAccess))
1398 break;
1399 var sym = node.left.expression;
1400 if (!(sym instanceof AST_SymbolRef))
1401 break;
1402 if (def.name.name != sym.name)
1403 break;
1404 if (!node.right.is_constant_expression(nearest_scope))
1405 break;
1406 var prop = node.left.property;
1407 if (prop instanceof AST_Node) {
1408 prop = prop.evaluate(compressor);
1409 }
1410 if (prop instanceof AST_Node)
1411 break;
1412 prop = "" + prop;
1413 var diff = compressor.option("ecma") < 2015
1414 && compressor.has_directive("use strict") ? function (node) {
1415 return node.key != prop && (node.key && node.key.name != prop);
1416 } : function (node) {
1417 return node.key && node.key.name != prop;
1418 };
1419 if (!def.value.properties.every(diff))
1420 break;
1421 var p = def.value.properties.filter(function (p) { return p.key === prop; })[0];
1422 if (!p) {
1423 def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
1424 key: prop,
1425 value: node.right
1426 }));
1427 } else {
1428 p.value = new AST_Sequence({
1429 start: p.start,
1430 expressions: [p.value.clone(), node.right.clone()],
1431 end: p.end
1432 });
1433 }
1434 exprs.shift();
1435 trimmed = true;
1436 } while (exprs.length);
1437 return trimmed && exprs;
1438 }
1439
1440 function join_consecutive_vars(statements) {
1441 var defs;
1442 for (var i = 0, j = -1, len = statements.length; i < len; i++) {
1443 var stat = statements[i];
1444 var prev = statements[j];
1445 if (stat instanceof AST_Definitions) {
1446 if (prev && prev.TYPE == stat.TYPE) {
1447 prev.definitions = prev.definitions.concat(stat.definitions);
1448 CHANGED = true;
1449 } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
1450 defs.definitions = defs.definitions.concat(stat.definitions);
1451 CHANGED = true;
1452 } else {
1453 statements[++j] = stat;
1454 defs = stat;
1455 }
1456 } else if (
1457 stat instanceof AST_Using
1458 && prev instanceof AST_Using
1459 && prev.await === stat.await
1460 ) {
1461 prev.definitions = prev.definitions.concat(stat.definitions);
1462 } else if (stat instanceof AST_Exit) {
1463 stat.value = extract_object_assignments(stat.value);
1464 } else if (stat instanceof AST_For) {
1465 var exprs = join_object_assignments(prev, stat.init);
1466 if (exprs) {
1467 CHANGED = true;
1468 stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
1469 statements[++j] = stat;
1470 } else if (
1471 prev instanceof AST_Var
1472 && (!stat.init || stat.init.TYPE == prev.TYPE)
1473 ) {
1474 if (stat.init) {
1475 prev.definitions = prev.definitions.concat(stat.init.definitions);
1476 }
1477 stat.init = prev;
1478 statements[j] = stat;
1479 CHANGED = true;
1480 } else if (
1481 defs instanceof AST_Var
1482 && stat.init instanceof AST_Var
1483 && declarations_only(stat.init)
1484 ) {
1485 defs.definitions = defs.definitions.concat(stat.init.definitions);
1486 stat.init = null;
1487 statements[++j] = stat;
1488 CHANGED = true;
1489 } else {
1490 statements[++j] = stat;
1491 }
1492 } else if (stat instanceof AST_ForIn) {
1493 stat.object = extract_object_assignments(stat.object);
1494 } else if (stat instanceof AST_If) {
1495 stat.condition = extract_object_assignments(stat.condition);
1496 } else if (stat instanceof AST_SimpleStatement) {
1497 var exprs = join_object_assignments(prev, stat.body);
1498 if (exprs) {
1499 CHANGED = true;
1500 if (!exprs.length)
1501 continue;
1502 stat.body = make_sequence(stat.body, exprs);
1503 }
1504 statements[++j] = stat;
1505 } else if (stat instanceof AST_Switch) {
1506 stat.expression = extract_object_assignments(stat.expression);
1507 } else if (stat instanceof AST_With) {
1508 stat.expression = extract_object_assignments(stat.expression);
1509 } else {
1510 statements[++j] = stat;
1511 }
1512 }
1513 statements.length = j + 1;
1514
1515 function extract_object_assignments(value) {
1516 statements[++j] = stat;
1517 var exprs = join_object_assignments(prev, value);
1518 if (exprs) {
1519 CHANGED = true;
1520 if (exprs.length) {
1521 return make_sequence(value, exprs);
1522 } else if (value instanceof AST_Sequence) {
1523 return value.tail_node().left;
1524 } else {
1525 return value.left;
1526 }
1527 }
1528 return value;
1529 }
1530 }
1531}
Note: See TracBrowser for help on using the repository browser.