source: frontend/node_modules/terser/lib/compress/reduce-vars.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: 24.7 KB
Line 
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_Accessor,
46 AST_Array,
47 AST_Assign,
48 AST_Await,
49 AST_Binary,
50 AST_Block,
51 AST_Call,
52 AST_Case,
53 AST_Chain,
54 AST_Class,
55 AST_ClassStaticBlock,
56 AST_ClassExpression,
57 AST_Conditional,
58 AST_Default,
59 AST_Defun,
60 AST_Destructuring,
61 AST_Do,
62 AST_Exit,
63 AST_Expansion,
64 AST_For,
65 AST_ForIn,
66 AST_If,
67 AST_LabeledStatement,
68 AST_Lambda,
69 AST_New,
70 AST_Node,
71 AST_Number,
72 AST_ObjectKeyVal,
73 AST_PropAccess,
74 AST_Scope,
75 AST_Sequence,
76 AST_SimpleStatement,
77 AST_Symbol,
78 AST_SymbolCatch,
79 AST_SymbolConst,
80 AST_SymbolDeclaration,
81 AST_SymbolDefun,
82 AST_SymbolFunarg,
83 AST_SymbolLambda,
84 AST_SymbolRef,
85 AST_This,
86 AST_Toplevel,
87 AST_Try,
88 AST_Unary,
89 AST_UnaryPrefix,
90 AST_UsingDef,
91 AST_VarDef,
92 AST_VarDefLike,
93 AST_While,
94 AST_Yield,
95
96 walk,
97 walk_body,
98 walk_parent,
99} from "../ast.js";
100import { HOP, make_node, make_void_0, noop } from "../utils/index.js";
101
102import { lazy_op, is_modified, is_lhs } from "./inference.js";
103import { INLINED, clear_flag } from "./compressor-flags.js";
104import { read_property, has_break_or_continue, is_recursive_ref } from "./common.js";
105
106/**
107 * Define the method AST_Node#reduce_vars, which goes through the AST in
108 * execution order to perform basic flow analysis
109 */
110function def_reduce_vars(node, func) {
111 node.DEFMETHOD("reduce_vars", func);
112}
113
114def_reduce_vars(AST_Node, noop);
115
116/** Clear definition properties */
117function reset_def(compressor, def) {
118 def.assignments = 0;
119 def.chained = false;
120 def.direct_access = false;
121 def.escaped = 0;
122 def.recursive_refs = 0;
123 def.references = [];
124 def.single_use = undefined;
125 if (
126 def.scope.pinned()
127 || (def.orig[0] instanceof AST_SymbolFunarg && def.scope.uses_arguments)
128 ) {
129 def.fixed = false;
130 } else if (def.orig[0] instanceof AST_SymbolConst || !compressor.exposed(def)) {
131 def.fixed = def.init;
132 } else {
133 def.fixed = false;
134 }
135}
136
137function reset_variables(tw, compressor, node) {
138 node.variables.forEach(function(def) {
139 reset_def(compressor, def);
140 if (def.fixed === null) {
141 tw.defs_to_safe_ids.set(def.id, tw.safe_ids);
142 mark(tw, def, true);
143 } else if (def.fixed) {
144 tw.loop_ids.set(def.id, tw.in_loop);
145 mark(tw, def, true);
146 }
147 });
148}
149
150function reset_block_variables(compressor, node) {
151 if (node.block_scope) node.block_scope.variables.forEach((def) => {
152 reset_def(compressor, def);
153 });
154}
155
156function push(tw) {
157 tw.safe_ids = Object.create(tw.safe_ids);
158}
159
160function pop(tw) {
161 tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
162}
163
164function mark(tw, def, safe) {
165 tw.safe_ids[def.id] = safe;
166}
167
168function safe_to_read(tw, def) {
169 if (def.single_use == "m") return false;
170 if (tw.safe_ids[def.id]) {
171 if (def.fixed == null) {
172 var orig = def.orig[0];
173 if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
174 def.fixed = make_void_0(orig);
175 }
176 return true;
177 }
178 return def.fixed instanceof AST_Defun;
179}
180
181function safe_to_assign(tw, def, scope, value) {
182 if (def.fixed === undefined) return true;
183 let def_safe_ids;
184 if (def.fixed === null
185 && (def_safe_ids = tw.defs_to_safe_ids.get(def.id))
186 ) {
187 def_safe_ids[def.id] = false;
188 tw.defs_to_safe_ids.delete(def.id);
189 return true;
190 }
191 if (!HOP(tw.safe_ids, def.id)) return false;
192 if (!safe_to_read(tw, def)) return false;
193 if (def.fixed === false) return false;
194 if (def.fixed != null && (!value || def.references.length > def.assignments)) return false;
195 if (def.fixed instanceof AST_Defun) {
196 return value instanceof AST_Node && def.fixed.parent_scope === scope;
197 }
198 return def.orig.every((sym) => {
199 return !(sym instanceof AST_SymbolConst
200 || sym instanceof AST_SymbolDefun
201 || sym instanceof AST_SymbolLambda);
202 });
203}
204
205function ref_once(tw, compressor, def) {
206 return compressor.option("unused")
207 && !def.scope.pinned()
208 && def.references.length - def.recursive_refs == 1
209 && tw.loop_ids.get(def.id) === tw.in_loop;
210}
211
212function is_immutable(value) {
213 if (!value) return false;
214 return value.is_constant()
215 || value instanceof AST_Lambda
216 || value instanceof AST_This;
217}
218
219// A definition "escapes" when its value can leave the point of use.
220// Example: `a = b || c`
221// In this example, "b" and "c" are escaping, because they're going into "a"
222//
223// def.escaped is != 0 when it escapes.
224//
225// When greater than 1, it means that N chained properties will be read off
226// of that def before an escape occurs. This is useful for evaluating
227// property accesses, where you need to know when to stop.
228function mark_escaped(tw, d, scope, node, value, level = 0, depth = 1) {
229 var parent = tw.parent(level);
230 if (value) {
231 if (value.is_constant()) return;
232 if (value instanceof AST_ClassExpression) return;
233 }
234
235 if (
236 parent instanceof AST_Assign && (parent.operator === "=" || parent.logical) && node === parent.right
237 || parent instanceof AST_Call && (node !== parent.expression || parent instanceof AST_New)
238 || parent instanceof AST_Exit && node === parent.value && node.scope !== d.scope
239 || parent instanceof AST_VarDefLike && node === parent.value
240 || parent instanceof AST_Yield && node === parent.value && node.scope !== d.scope
241 ) {
242 if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
243 if (!d.escaped || d.escaped > depth) d.escaped = depth;
244 return;
245 } else if (
246 parent instanceof AST_Array
247 || parent instanceof AST_Await
248 || parent instanceof AST_Binary && lazy_op.has(parent.operator)
249 || parent instanceof AST_Conditional && node !== parent.condition
250 || parent instanceof AST_Expansion
251 || parent instanceof AST_Sequence && node === parent.tail_node()
252 ) {
253 mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
254 } else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
255 var obj = tw.parent(level + 1);
256
257 mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
258 } else if (parent instanceof AST_PropAccess && node === parent.expression) {
259 value = read_property(value, parent.property);
260
261 mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
262 if (value) return;
263 }
264
265 if (level > 0) return;
266 if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
267 if (parent instanceof AST_SimpleStatement) return;
268
269 d.direct_access = true;
270}
271
272const suppress = node => walk(node, node => {
273 if (!(node instanceof AST_Symbol)) return;
274 var d = node.definition();
275 if (!d) return;
276 if (node instanceof AST_SymbolRef) d.references.push(node);
277 d.fixed = false;
278});
279
280def_reduce_vars(AST_Accessor, function(tw, descend, compressor) {
281 push(tw);
282 reset_variables(tw, compressor, this);
283 descend();
284 pop(tw);
285 return true;
286});
287
288def_reduce_vars(AST_Assign, function(tw, descend, compressor) {
289 var node = this;
290 if (node.left instanceof AST_Destructuring) {
291 suppress(node.left);
292 return;
293 }
294
295 const finish_walk = () => {
296 if (node.logical) {
297 node.left.walk(tw);
298
299 push(tw);
300 node.right.walk(tw);
301 pop(tw);
302
303 return true;
304 }
305 };
306
307 var sym = node.left;
308 if (!(sym instanceof AST_SymbolRef)) return finish_walk();
309
310 var def = sym.definition();
311 var safe = safe_to_assign(tw, def, sym.scope, node.right);
312 def.assignments++;
313 if (!safe) return finish_walk();
314
315 var fixed = def.fixed;
316 if (!fixed && node.operator != "=" && !node.logical) return finish_walk();
317
318 var eq = node.operator == "=";
319 var value = eq ? node.right : node;
320 if (is_modified(compressor, tw, node, value, 0)) return finish_walk();
321
322 def.references.push(sym);
323
324 if (!node.logical) {
325 if (!eq) def.chained = true;
326
327 def.fixed = eq ? function() {
328 return node.right;
329 } : function() {
330 return make_node(AST_Binary, node, {
331 operator: node.operator.slice(0, -1),
332 left: fixed instanceof AST_Node ? fixed : fixed(),
333 right: node.right
334 });
335 };
336 }
337
338 if (node.logical) {
339 mark(tw, def, false);
340 push(tw);
341 node.right.walk(tw);
342 pop(tw);
343 return true;
344 }
345
346 mark(tw, def, false);
347 node.right.walk(tw);
348 mark(tw, def, true);
349
350 mark_escaped(tw, def, sym.scope, node, value, 0, 1);
351
352 return true;
353});
354
355def_reduce_vars(AST_Binary, function(tw) {
356 if (!lazy_op.has(this.operator)) return;
357 this.left.walk(tw);
358 push(tw);
359 this.right.walk(tw);
360 pop(tw);
361 return true;
362});
363
364def_reduce_vars(AST_Block, function(tw, descend, compressor) {
365 reset_block_variables(compressor, this);
366});
367
368def_reduce_vars(AST_Case, function(tw) {
369 push(tw);
370 this.expression.walk(tw);
371 pop(tw);
372 push(tw);
373 walk_body(this, tw);
374 pop(tw);
375 return true;
376});
377
378def_reduce_vars(AST_Class, function(tw, descend) {
379 clear_flag(this, INLINED);
380 push(tw);
381 descend();
382 pop(tw);
383 return true;
384});
385
386def_reduce_vars(AST_ClassStaticBlock, function(tw, descend, compressor) {
387 reset_block_variables(compressor, this);
388});
389
390def_reduce_vars(AST_Conditional, function(tw) {
391 this.condition.walk(tw);
392 push(tw);
393 this.consequent.walk(tw);
394 pop(tw);
395 push(tw);
396 this.alternative.walk(tw);
397 pop(tw);
398 return true;
399});
400
401def_reduce_vars(AST_Chain, function(tw, descend) {
402 // Chains' conditions apply left-to-right, cumulatively.
403 // If we walk normally we don't go in that order because we would pop before pushing again
404 // Solution: AST_PropAccess and AST_Call push when they are optional, and never pop.
405 // Then we pop everything when they are done being walked.
406 const safe_ids = tw.safe_ids;
407
408 descend();
409
410 // Unroll back to start
411 tw.safe_ids = safe_ids;
412 return true;
413});
414
415def_reduce_vars(AST_Call, function (tw) {
416 this.expression.walk(tw);
417
418 if (this.optional) {
419 // Never pop -- it's popped at AST_Chain above
420 push(tw);
421 }
422
423 for (const arg of this.args) arg.walk(tw);
424
425 return true;
426});
427
428def_reduce_vars(AST_PropAccess, function (tw) {
429 if (!this.optional) return;
430
431 this.expression.walk(tw);
432
433 // Never pop -- it's popped at AST_Chain above
434 push(tw);
435
436 if (this.property instanceof AST_Node) this.property.walk(tw);
437
438 return true;
439});
440
441def_reduce_vars(AST_Default, function(tw, descend) {
442 push(tw);
443 descend();
444 pop(tw);
445 return true;
446});
447
448function mark_lambda(tw, descend, compressor) {
449 clear_flag(this, INLINED);
450 push(tw);
451 reset_variables(tw, compressor, this);
452
453 var iife;
454 if (!this.name
455 && !this.uses_arguments
456 && !this.pinned()
457 && (iife = tw.parent()) instanceof AST_Call
458 && iife.expression === this
459 && !iife.args.some(arg => arg instanceof AST_Expansion)
460 && this.argnames.every(arg_name => arg_name instanceof AST_Symbol)
461 ) {
462 // Virtually turn IIFE parameters into variable definitions:
463 // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
464 // So existing transformation rules can work on them.
465 this.argnames.forEach((arg, i) => {
466 if (!arg.definition) return;
467 var d = arg.definition();
468 // Avoid setting fixed when there's more than one origin for a variable value
469 if (d.orig.length > 1) return;
470 if (d.fixed === undefined && (!this.uses_arguments || tw.has_directive("use strict"))) {
471 d.fixed = function() {
472 return iife.args[i] || make_void_0(iife);
473 };
474 tw.loop_ids.set(d.id, tw.in_loop);
475 mark(tw, d, true);
476 } else {
477 d.fixed = false;
478 }
479 });
480 }
481
482 descend();
483 pop(tw);
484
485 handle_defined_after_hoist(this);
486
487 return true;
488}
489
490/**
491 * It's possible for a hoisted function to use something that's not defined yet. Example:
492 *
493 * hoisted();
494 * var defined_after = true;
495 * function hoisted() {
496 * // use defined_after
497 * }
498 *
499 * Or even indirectly:
500 *
501 * B();
502 * var defined_after = true;
503 * function A() {
504 * // use defined_after
505 * }
506 * function B() {
507 * A();
508 * }
509 *
510 * Access a variable before declaration will either throw a ReferenceError
511 * (if the variable is declared with `let` or `const`),
512 * or get an `undefined` (if the variable is declared with `var`).
513 *
514 * If the variable is inlined into the function, the behavior will change.
515 *
516 * This function is called on the parent to disallow inlining of such variables,
517 */
518function handle_defined_after_hoist(parent) {
519 const defuns = [];
520 walk(parent, node => {
521 if (node === parent) return;
522 if (node instanceof AST_Defun) {
523 defuns.push(node);
524 return true;
525 }
526 if (
527 node instanceof AST_Scope
528 || node instanceof AST_SimpleStatement
529 ) return true;
530 });
531
532 // `defun` id to array of `defun` it uses
533 const defun_dependencies_map = new Map();
534 // `defun` id to array of enclosing `def` that are used by the function
535 const dependencies_map = new Map();
536 // all symbol ids that will be tracked for read/write
537 const symbols_of_interest = new Set();
538 const defuns_of_interest = new Set();
539
540 for (const defun of defuns) {
541 const fname_def = defun.name.definition();
542 const enclosing_defs = [];
543
544 for (const def of defun.enclosed) {
545 if (
546 def.fixed === false
547 || def === fname_def
548 || def.scope.get_defun_scope() !== parent
549 ) {
550 continue;
551 }
552
553 symbols_of_interest.add(def.id);
554
555 // found a reference to another function
556 if (
557 def.assignments === 0
558 && def.orig.length === 1
559 && def.orig[0] instanceof AST_SymbolDefun
560 ) {
561 defuns_of_interest.add(def.id);
562 symbols_of_interest.add(def.id);
563
564 defuns_of_interest.add(fname_def.id);
565 symbols_of_interest.add(fname_def.id);
566
567 if (!defun_dependencies_map.has(fname_def.id)) {
568 defun_dependencies_map.set(fname_def.id, []);
569 }
570 defun_dependencies_map.get(fname_def.id).push(def.id);
571
572 continue;
573 }
574
575 enclosing_defs.push(def);
576 }
577
578 if (enclosing_defs.length) {
579 dependencies_map.set(fname_def.id, enclosing_defs);
580 defuns_of_interest.add(fname_def.id);
581 symbols_of_interest.add(fname_def.id);
582 }
583 }
584
585 // No defuns use outside constants
586 if (!dependencies_map.size) {
587 return;
588 }
589
590 // Increment to count "symbols of interest" (defuns or defs) that we found.
591 // These are tracked in AST order so we can check which is after which.
592 let symbol_index = 1;
593 // Map a defun ID to its first read (a `symbol_index`)
594 const defun_first_read_map = new Map();
595 // Map a symbol ID to its last write (a `symbol_index`)
596 const symbol_last_write_map = new Map();
597
598 walk_parent(parent, (node, walk_info) => {
599 if (node instanceof AST_Symbol && node.thedef) {
600 const id = node.definition().id;
601
602 symbol_index++;
603
604 // Track last-writes to symbols
605 if (symbols_of_interest.has(id)) {
606 if (node instanceof AST_SymbolDeclaration || is_lhs(node, walk_info.parent())) {
607 symbol_last_write_map.set(id, symbol_index);
608 }
609 }
610
611 // Track first-reads of defuns (refined later)
612 if (defuns_of_interest.has(id)) {
613 if (!defun_first_read_map.has(id) && !is_recursive_ref(walk_info, id)) {
614 defun_first_read_map.set(id, symbol_index);
615 }
616 }
617 }
618 });
619
620 // Refine `defun_first_read_map` to be as high as possible
621 for (const [defun, defun_first_read] of defun_first_read_map) {
622 // Update all dependencies of `defun`
623 const queue = new Set(defun_dependencies_map.get(defun));
624 for (const enclosed_defun of queue) {
625 let enclosed_defun_first_read = defun_first_read_map.get(enclosed_defun);
626 if (enclosed_defun_first_read != null && enclosed_defun_first_read < defun_first_read) {
627 continue;
628 }
629
630 defun_first_read_map.set(enclosed_defun, defun_first_read);
631
632 for (const enclosed_enclosed_defun of defun_dependencies_map.get(enclosed_defun) || []) {
633 queue.add(enclosed_enclosed_defun);
634 }
635 }
636 }
637
638 // ensure write-then-read order, otherwise clear `fixed`
639 // This is safe because last-writes (found_symbol_writes) are assumed to be as late as possible, and first-reads (defun_first_read_map) are assumed to be as early as possible.
640 for (const [defun, defs] of dependencies_map) {
641 const defun_first_read = defun_first_read_map.get(defun);
642 if (defun_first_read === undefined) {
643 continue;
644 }
645
646 for (const def of defs) {
647 if (def.fixed === false) {
648 continue;
649 }
650
651 let def_last_write = symbol_last_write_map.get(def.id) || 0;
652
653 if (defun_first_read < def_last_write) {
654 def.fixed = false;
655 }
656 }
657 }
658}
659
660def_reduce_vars(AST_Lambda, mark_lambda);
661
662def_reduce_vars(AST_Do, function(tw, descend, compressor) {
663 reset_block_variables(compressor, this);
664 const saved_loop = tw.in_loop;
665 tw.in_loop = this;
666 push(tw);
667 this.body.walk(tw);
668 if (has_break_or_continue(this)) {
669 pop(tw);
670 push(tw);
671 }
672 this.condition.walk(tw);
673 pop(tw);
674 tw.in_loop = saved_loop;
675 return true;
676});
677
678def_reduce_vars(AST_For, function(tw, descend, compressor) {
679 reset_block_variables(compressor, this);
680 if (this.init) this.init.walk(tw);
681 const saved_loop = tw.in_loop;
682 tw.in_loop = this;
683 push(tw);
684 if (this.condition) this.condition.walk(tw);
685 this.body.walk(tw);
686 if (this.step) {
687 if (has_break_or_continue(this)) {
688 pop(tw);
689 push(tw);
690 }
691 this.step.walk(tw);
692 }
693 pop(tw);
694 tw.in_loop = saved_loop;
695 return true;
696});
697
698def_reduce_vars(AST_ForIn, function(tw, descend, compressor) {
699 reset_block_variables(compressor, this);
700 suppress(this.init);
701 this.object.walk(tw);
702 const saved_loop = tw.in_loop;
703 tw.in_loop = this;
704 push(tw);
705 this.body.walk(tw);
706 pop(tw);
707 tw.in_loop = saved_loop;
708 return true;
709});
710
711def_reduce_vars(AST_If, function(tw) {
712 this.condition.walk(tw);
713 push(tw);
714 this.body.walk(tw);
715 pop(tw);
716 if (this.alternative) {
717 push(tw);
718 this.alternative.walk(tw);
719 pop(tw);
720 }
721 return true;
722});
723
724def_reduce_vars(AST_LabeledStatement, function(tw) {
725 push(tw);
726 this.body.walk(tw);
727 pop(tw);
728 return true;
729});
730
731def_reduce_vars(AST_SymbolCatch, function() {
732 this.definition().fixed = false;
733});
734
735def_reduce_vars(AST_SymbolRef, function(tw, descend, compressor) {
736 var d = this.definition();
737 d.references.push(this);
738 if (d.references.length == 1
739 && !d.fixed
740 && d.orig[0] instanceof AST_SymbolDefun) {
741 tw.loop_ids.set(d.id, tw.in_loop);
742 }
743 var fixed_value;
744 if (d.fixed === undefined || !safe_to_read(tw, d)) {
745 d.fixed = false;
746 } else if (d.fixed) {
747 fixed_value = this.fixed_value();
748 if (
749 fixed_value instanceof AST_Lambda
750 && is_recursive_ref(tw, d)
751 ) {
752 d.recursive_refs++;
753 } else if (fixed_value
754 && !compressor.exposed(d)
755 && ref_once(tw, compressor, d)
756 ) {
757 d.single_use =
758 fixed_value instanceof AST_Lambda && !fixed_value.pinned()
759 || fixed_value instanceof AST_Class
760 || d.scope === this.scope && fixed_value.is_constant_expression();
761 } else {
762 d.single_use = false;
763 }
764 if (is_modified(compressor, tw, this, fixed_value, 0, is_immutable(fixed_value))) {
765 if (d.single_use) {
766 d.single_use = "m";
767 } else {
768 d.fixed = false;
769 }
770 }
771 }
772 mark_escaped(tw, d, this.scope, this, fixed_value, 0, 1);
773});
774
775def_reduce_vars(AST_Toplevel, function(tw, descend, compressor) {
776 this.globals.forEach(function(def) {
777 reset_def(compressor, def);
778 });
779 reset_variables(tw, compressor, this);
780 descend();
781 handle_defined_after_hoist(this);
782 return true;
783});
784
785def_reduce_vars(AST_Try, function(tw, descend, compressor) {
786 reset_block_variables(compressor, this);
787 push(tw);
788 this.body.walk(tw);
789 pop(tw);
790 if (this.bcatch) {
791 push(tw);
792 this.bcatch.walk(tw);
793 pop(tw);
794 }
795 if (this.bfinally) this.bfinally.walk(tw);
796 return true;
797});
798
799def_reduce_vars(AST_Unary, function(tw) {
800 var node = this;
801 if (node.operator !== "++" && node.operator !== "--") return;
802 var exp = node.expression;
803 if (!(exp instanceof AST_SymbolRef)) return;
804 var def = exp.definition();
805 var safe = safe_to_assign(tw, def, exp.scope, true);
806 def.assignments++;
807 if (!safe) return;
808 var fixed = def.fixed;
809 if (!fixed) return;
810 def.references.push(exp);
811 def.chained = true;
812 def.fixed = function() {
813 return make_node(AST_Binary, node, {
814 operator: node.operator.slice(0, -1),
815 left: make_node(AST_UnaryPrefix, node, {
816 operator: "+",
817 expression: fixed instanceof AST_Node ? fixed : fixed()
818 }),
819 right: make_node(AST_Number, node, {
820 value: 1
821 })
822 });
823 };
824 mark(tw, def, true);
825 return true;
826});
827
828def_reduce_vars(AST_VarDef, function(tw, descend) {
829 var node = this;
830 if (node.name instanceof AST_Destructuring) {
831 suppress(node.name);
832 return;
833 }
834 var d = node.name.definition();
835 if (node.value) {
836 if (safe_to_assign(tw, d, node.name.scope, node.value)) {
837 d.fixed = function() {
838 return node.value;
839 };
840 tw.loop_ids.set(d.id, tw.in_loop);
841 mark(tw, d, false);
842 descend();
843 mark(tw, d, true);
844 return true;
845 } else {
846 d.fixed = false;
847 }
848 }
849});
850
851def_reduce_vars(AST_UsingDef, function() {
852 suppress(this.name);
853});
854
855def_reduce_vars(AST_While, function(tw, descend, compressor) {
856 reset_block_variables(compressor, this);
857 const saved_loop = tw.in_loop;
858 tw.in_loop = this;
859 push(tw);
860 descend();
861 pop(tw);
862 tw.in_loop = saved_loop;
863 return true;
864});
Note: See TracBrowser for help on using the repository browser.