source: imaps-frontend/node_modules/terser/lib/compress/tighten-body.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

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