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