terser 5.32.0 → 5.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/bundle.min.js +105 -69
- package/lib/compress/common.js +2 -2
- package/lib/compress/reduce-vars.js +110 -75
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/dist/bundle.min.js
CHANGED
@@ -13291,9 +13291,9 @@ function is_reachable(scope_node, defs) {
|
|
13291
13291
|
}
|
13292
13292
|
|
13293
13293
|
/** Check if a ref refers to the name of a function/class it's defined within */
|
13294
|
-
function is_recursive_ref(
|
13294
|
+
function is_recursive_ref(tw, def) {
|
13295
13295
|
var node;
|
13296
|
-
for (var i = 0; node =
|
13296
|
+
for (var i = 0; node = tw.parent(i); i++) {
|
13297
13297
|
if (node instanceof AST_Lambda || node instanceof AST_Class) {
|
13298
13298
|
var name = node.name;
|
13299
13299
|
if (name && name.definition() === def) {
|
@@ -16147,28 +16147,50 @@ function mark_lambda(tw, descend, compressor) {
|
|
16147
16147
|
* // use defined_after
|
16148
16148
|
* }
|
16149
16149
|
*
|
16150
|
-
*
|
16150
|
+
* Or even indirectly:
|
16151
|
+
*
|
16152
|
+
* B();
|
16153
|
+
* var defined_after = true;
|
16154
|
+
* function A() {
|
16155
|
+
* // use defined_after
|
16156
|
+
* }
|
16157
|
+
* function B() {
|
16158
|
+
* A();
|
16159
|
+
* }
|
16160
|
+
*
|
16161
|
+
* Access a variable before declaration will either throw a ReferenceError
|
16162
|
+
* (if the variable is declared with `let` or `const`),
|
16163
|
+
* or get an `undefined` (if the variable is declared with `var`).
|
16164
|
+
*
|
16165
|
+
* If the variable is inlined into the function, the behavior will change.
|
16166
|
+
*
|
16167
|
+
* This function is called on the parent to disallow inlining of such variables,
|
16151
16168
|
*/
|
16152
16169
|
function handle_defined_after_hoist(parent) {
|
16153
16170
|
const defuns = [];
|
16154
16171
|
walk(parent, node => {
|
16155
16172
|
if (node === parent) return;
|
16156
|
-
if (node instanceof AST_Defun)
|
16173
|
+
if (node instanceof AST_Defun) {
|
16174
|
+
defuns.push(node);
|
16175
|
+
return true;
|
16176
|
+
}
|
16157
16177
|
if (
|
16158
16178
|
node instanceof AST_Scope
|
16159
16179
|
|| node instanceof AST_SimpleStatement
|
16160
16180
|
) return true;
|
16161
16181
|
});
|
16162
16182
|
|
16183
|
+
// `defun` id to array of `defun` it uses
|
16184
|
+
const defun_dependencies_map = new Map();
|
16185
|
+
// `defun` id to array of enclosing `def` that are used by the function
|
16186
|
+
const dependencies_map = new Map();
|
16187
|
+
// all symbol ids that will be tracked for read/write
|
16163
16188
|
const symbols_of_interest = new Set();
|
16164
16189
|
const defuns_of_interest = new Set();
|
16165
|
-
const potential_conflicts = [];
|
16166
16190
|
|
16167
16191
|
for (const defun of defuns) {
|
16168
16192
|
const fname_def = defun.name.definition();
|
16169
|
-
const
|
16170
|
-
d => d !== defun && d.enclosed.indexOf(fname_def) !== -1
|
16171
|
-
);
|
16193
|
+
const enclosing_defs = [];
|
16172
16194
|
|
16173
16195
|
for (const def of defun.enclosed) {
|
16174
16196
|
if (
|
@@ -16179,93 +16201,107 @@ function handle_defined_after_hoist(parent) {
|
|
16179
16201
|
continue;
|
16180
16202
|
}
|
16181
16203
|
|
16182
|
-
|
16204
|
+
symbols_of_interest.add(def.id);
|
16205
|
+
|
16206
|
+
// found a reference to another function
|
16183
16207
|
if (
|
16184
16208
|
def.assignments === 0
|
16185
16209
|
&& def.orig.length === 1
|
16186
16210
|
&& def.orig[0] instanceof AST_SymbolDefun
|
16187
16211
|
) {
|
16188
|
-
|
16189
|
-
|
16212
|
+
defuns_of_interest.add(def.id);
|
16213
|
+
symbols_of_interest.add(def.id);
|
16214
|
+
|
16215
|
+
defuns_of_interest.add(fname_def.id);
|
16216
|
+
symbols_of_interest.add(fname_def.id);
|
16217
|
+
|
16218
|
+
if (!defun_dependencies_map.has(fname_def.id)) {
|
16219
|
+
defun_dependencies_map.set(fname_def.id, []);
|
16220
|
+
}
|
16221
|
+
defun_dependencies_map.get(fname_def.id).push(def.id);
|
16190
16222
|
|
16191
|
-
if (found_self_ref_in_other_defuns) {
|
16192
|
-
def.fixed = false;
|
16193
16223
|
continue;
|
16194
16224
|
}
|
16195
16225
|
|
16196
|
-
|
16197
|
-
|
16198
|
-
|
16226
|
+
enclosing_defs.push(def);
|
16227
|
+
}
|
16228
|
+
|
16229
|
+
if (enclosing_defs.length) {
|
16230
|
+
dependencies_map.set(fname_def.id, enclosing_defs);
|
16231
|
+
defuns_of_interest.add(fname_def.id);
|
16199
16232
|
symbols_of_interest.add(fname_def.id);
|
16200
|
-
defuns_of_interest.add(defun);
|
16201
16233
|
}
|
16202
16234
|
}
|
16203
16235
|
|
16204
|
-
//
|
16205
|
-
if (
|
16206
|
-
|
16207
|
-
|
16208
|
-
const found_symbols = [];
|
16209
|
-
// Indices of `found_symbols` which are writes
|
16210
|
-
const found_symbol_writes = new Set();
|
16211
|
-
// Defun ranges are recorded because we don't care if a function uses the def internally
|
16212
|
-
const defun_ranges = new Map();
|
16236
|
+
// No defuns use outside constants
|
16237
|
+
if (!dependencies_map.size) {
|
16238
|
+
return;
|
16239
|
+
}
|
16213
16240
|
|
16214
|
-
|
16215
|
-
|
16216
|
-
|
16217
|
-
|
16218
|
-
|
16219
|
-
|
16241
|
+
// Increment to count "symbols of interest" (defuns or defs) that we found.
|
16242
|
+
// These are tracked in AST order so we can check which is after which.
|
16243
|
+
let symbol_index = 1;
|
16244
|
+
// Map a defun ID to its first read (a `symbol_index`)
|
16245
|
+
const defun_first_read_map = new Map();
|
16246
|
+
// Map a symbol ID to its last write (a `symbol_index`)
|
16247
|
+
const symbol_last_write_map = new Map();
|
16220
16248
|
|
16221
|
-
|
16222
|
-
|
16223
|
-
|
16224
|
-
// if we found a defun on the list, mark IN_DEFUN=id and descend
|
16249
|
+
walk_parent(parent, (node, walk_info) => {
|
16250
|
+
if (node instanceof AST_Symbol && node.thedef) {
|
16251
|
+
const id = node.definition().id;
|
16225
16252
|
|
16226
|
-
|
16227
|
-
|
16228
|
-
|
16229
|
-
|
16230
|
-
|
16231
|
-
|
16232
|
-
found_symbols.push(id);
|
16253
|
+
symbol_index++;
|
16254
|
+
|
16255
|
+
// Track last-writes to symbols
|
16256
|
+
if (symbols_of_interest.has(id)) {
|
16257
|
+
if (node instanceof AST_SymbolDeclaration || is_lhs(node, walk_info.parent())) {
|
16258
|
+
symbol_last_write_map.set(id, symbol_index);
|
16233
16259
|
}
|
16234
16260
|
}
|
16235
|
-
})));
|
16236
16261
|
|
16237
|
-
|
16238
|
-
|
16262
|
+
// Track first-reads of defuns (refined later)
|
16263
|
+
if (defuns_of_interest.has(id)) {
|
16264
|
+
if (!defun_first_read_map.has(id) && !is_recursive_ref(walk_info, id)) {
|
16265
|
+
defun_first_read_map.set(id, symbol_index);
|
16266
|
+
}
|
16267
|
+
}
|
16268
|
+
}
|
16269
|
+
});
|
16239
16270
|
|
16240
|
-
|
16241
|
-
|
16242
|
-
|
16271
|
+
// Refine `defun_first_read_map` to be as high as possible
|
16272
|
+
for (const [defun, defun_first_read] of defun_first_read_map) {
|
16273
|
+
// Update all depdencies of `defun`
|
16274
|
+
const queue = new Set(defun_dependencies_map.get(defun));
|
16275
|
+
for (const enclosed_defun of queue) {
|
16276
|
+
let enclosed_defun_first_read = defun_first_read_map.get(enclosed_defun);
|
16277
|
+
if (enclosed_defun_first_read != null && enclosed_defun_first_read < defun_first_read) {
|
16278
|
+
continue;
|
16279
|
+
}
|
16243
16280
|
|
16244
|
-
|
16245
|
-
index = found_symbols.indexOf(sym_id, index);
|
16281
|
+
defun_first_read_map.set(enclosed_defun, defun_first_read);
|
16246
16282
|
|
16247
|
-
|
16248
|
-
|
16249
|
-
|
16250
|
-
|
16251
|
-
|
16252
|
-
} else if (must_be_write && !found_symbol_writes.has(index)) {
|
16253
|
-
index++;
|
16254
|
-
continue;
|
16255
|
-
} else {
|
16256
|
-
break;
|
16257
|
-
}
|
16258
|
-
}
|
16283
|
+
for (const enclosed_enclosed_defun of defun_dependencies_map.get(enclosed_defun) || []) {
|
16284
|
+
queue.add(enclosed_enclosed_defun);
|
16285
|
+
}
|
16286
|
+
}
|
16287
|
+
}
|
16259
16288
|
|
16260
|
-
|
16261
|
-
|
16289
|
+
// ensure write-then-read order, otherwise clear `fixed`
|
16290
|
+
// 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.
|
16291
|
+
for (const [defun, defs] of dependencies_map) {
|
16292
|
+
const defun_first_read = defun_first_read_map.get(defun);
|
16293
|
+
if (defun_first_read === undefined) {
|
16294
|
+
continue;
|
16295
|
+
}
|
16262
16296
|
|
16263
|
-
|
16264
|
-
|
16297
|
+
for (const def of defs) {
|
16298
|
+
if (def.fixed === false) {
|
16299
|
+
continue;
|
16300
|
+
}
|
16265
16301
|
|
16266
|
-
|
16302
|
+
let def_last_write = symbol_last_write_map.get(def.id) || 0;
|
16267
16303
|
|
16268
|
-
if (
|
16304
|
+
if (defun_first_read < def_last_write) {
|
16269
16305
|
def.fixed = false;
|
16270
16306
|
}
|
16271
16307
|
}
|
package/lib/compress/common.js
CHANGED
@@ -333,9 +333,9 @@ export function is_reachable(scope_node, defs) {
|
|
333
333
|
}
|
334
334
|
|
335
335
|
/** Check if a ref refers to the name of a function/class it's defined within */
|
336
|
-
export function is_recursive_ref(
|
336
|
+
export function is_recursive_ref(tw, def) {
|
337
337
|
var node;
|
338
|
-
for (var i = 0; node =
|
338
|
+
for (var i = 0; node = tw.parent(i); i++) {
|
339
339
|
if (node instanceof AST_Lambda || node instanceof AST_Class) {
|
340
340
|
var name = node.name;
|
341
341
|
if (name && name.definition() === def) {
|
@@ -94,8 +94,7 @@ import {
|
|
94
94
|
|
95
95
|
walk,
|
96
96
|
walk_body,
|
97
|
-
|
98
|
-
TreeWalker,
|
97
|
+
walk_parent,
|
99
98
|
} from "../ast.js";
|
100
99
|
import { HOP, make_node, noop } from "../utils/index.js";
|
101
100
|
|
@@ -496,28 +495,50 @@ function mark_lambda(tw, descend, compressor) {
|
|
496
495
|
* // use defined_after
|
497
496
|
* }
|
498
497
|
*
|
499
|
-
*
|
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,
|
500
516
|
*/
|
501
517
|
function handle_defined_after_hoist(parent) {
|
502
518
|
const defuns = [];
|
503
519
|
walk(parent, node => {
|
504
520
|
if (node === parent) return;
|
505
|
-
if (node instanceof AST_Defun)
|
521
|
+
if (node instanceof AST_Defun) {
|
522
|
+
defuns.push(node);
|
523
|
+
return true;
|
524
|
+
}
|
506
525
|
if (
|
507
526
|
node instanceof AST_Scope
|
508
527
|
|| node instanceof AST_SimpleStatement
|
509
528
|
) return true;
|
510
529
|
});
|
511
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
|
512
536
|
const symbols_of_interest = new Set();
|
513
537
|
const defuns_of_interest = new Set();
|
514
|
-
const potential_conflicts = [];
|
515
538
|
|
516
539
|
for (const defun of defuns) {
|
517
540
|
const fname_def = defun.name.definition();
|
518
|
-
const
|
519
|
-
d => d !== defun && d.enclosed.indexOf(fname_def) !== -1
|
520
|
-
);
|
541
|
+
const enclosing_defs = [];
|
521
542
|
|
522
543
|
for (const def of defun.enclosed) {
|
523
544
|
if (
|
@@ -528,93 +549,107 @@ function handle_defined_after_hoist(parent) {
|
|
528
549
|
continue;
|
529
550
|
}
|
530
551
|
|
531
|
-
|
552
|
+
symbols_of_interest.add(def.id);
|
553
|
+
|
554
|
+
// found a reference to another function
|
532
555
|
if (
|
533
556
|
def.assignments === 0
|
534
557
|
&& def.orig.length === 1
|
535
558
|
&& def.orig[0] instanceof AST_SymbolDefun
|
536
559
|
) {
|
537
|
-
|
538
|
-
|
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);
|
539
570
|
|
540
|
-
if (found_self_ref_in_other_defuns) {
|
541
|
-
def.fixed = false;
|
542
571
|
continue;
|
543
572
|
}
|
544
573
|
|
545
|
-
|
546
|
-
|
547
|
-
|
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);
|
548
580
|
symbols_of_interest.add(fname_def.id);
|
549
|
-
defuns_of_interest.add(defun);
|
550
581
|
}
|
551
582
|
}
|
552
583
|
|
553
|
-
//
|
554
|
-
if (
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
const id = node.definition().id;
|
577
|
-
if (symbols_of_interest.has(id)) {
|
578
|
-
if (node instanceof AST_SymbolDeclaration || is_lhs(node, tw)) {
|
579
|
-
found_symbol_writes.add(found_symbols.length);
|
580
|
-
}
|
581
|
-
found_symbols.push(id);
|
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);
|
582
607
|
}
|
583
608
|
}
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
// find the index in `found_symbols`, with some special rules:
|
590
|
-
const find = (sym_id, starting_at = 0, must_be_write = false) => {
|
591
|
-
let index = starting_at;
|
592
|
-
|
593
|
-
for (;;) {
|
594
|
-
index = found_symbols.indexOf(sym_id, index);
|
595
|
-
|
596
|
-
if (index === -1) {
|
597
|
-
break;
|
598
|
-
} else if (index >= defun_range.start && index < defun_range.end) {
|
599
|
-
index = defun_range.end;
|
600
|
-
continue;
|
601
|
-
} else if (must_be_write && !found_symbol_writes.has(index)) {
|
602
|
-
index++;
|
603
|
-
continue;
|
604
|
-
} else {
|
605
|
-
break;
|
606
|
-
}
|
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);
|
607
614
|
}
|
615
|
+
}
|
616
|
+
}
|
617
|
+
});
|
608
618
|
|
609
|
-
|
610
|
-
|
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);
|
611
630
|
|
612
|
-
const
|
613
|
-
|
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
|
+
}
|
614
649
|
|
615
|
-
|
650
|
+
let def_last_write = symbol_last_write_map.get(def.id) || 0;
|
616
651
|
|
617
|
-
if (
|
652
|
+
if (defun_first_read < def_last_write) {
|
618
653
|
def.fixed = false;
|
619
654
|
}
|
620
655
|
}
|