feat: refactoring project
This commit is contained in:
177
node_modules/terser/lib/compress/reduce-vars.js
generated
vendored
177
node_modules/terser/lib/compress/reduce-vars.js
generated
vendored
@@ -94,8 +94,7 @@ import {
|
||||
|
||||
walk,
|
||||
walk_body,
|
||||
|
||||
TreeWalker,
|
||||
walk_parent,
|
||||
} from "../ast.js";
|
||||
import { HOP, make_node, noop } from "../utils/index.js";
|
||||
|
||||
@@ -496,28 +495,50 @@ function mark_lambda(tw, descend, compressor) {
|
||||
* // use defined_after
|
||||
* }
|
||||
*
|
||||
* This function is called on the parent to handle this issue.
|
||||
* Or even indirectly:
|
||||
*
|
||||
* B();
|
||||
* var defined_after = true;
|
||||
* function A() {
|
||||
* // use defined_after
|
||||
* }
|
||||
* function B() {
|
||||
* A();
|
||||
* }
|
||||
*
|
||||
* Access a variable before declaration will either throw a ReferenceError
|
||||
* (if the variable is declared with `let` or `const`),
|
||||
* or get an `undefined` (if the variable is declared with `var`).
|
||||
*
|
||||
* If the variable is inlined into the function, the behavior will change.
|
||||
*
|
||||
* This function is called on the parent to disallow inlining of such variables,
|
||||
*/
|
||||
function handle_defined_after_hoist(parent) {
|
||||
const defuns = [];
|
||||
walk(parent, node => {
|
||||
if (node === parent) return;
|
||||
if (node instanceof AST_Defun) defuns.push(node);
|
||||
if (node instanceof AST_Defun) {
|
||||
defuns.push(node);
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
node instanceof AST_Scope
|
||||
|| node instanceof AST_SimpleStatement
|
||||
) return true;
|
||||
});
|
||||
|
||||
// `defun` id to array of `defun` it uses
|
||||
const defun_dependencies_map = new Map();
|
||||
// `defun` id to array of enclosing `def` that are used by the function
|
||||
const dependencies_map = new Map();
|
||||
// all symbol ids that will be tracked for read/write
|
||||
const symbols_of_interest = new Set();
|
||||
const defuns_of_interest = new Set();
|
||||
const potential_conflicts = [];
|
||||
|
||||
for (const defun of defuns) {
|
||||
const fname_def = defun.name.definition();
|
||||
const found_self_ref_in_other_defuns = defuns.some(
|
||||
d => d !== defun && d.enclosed.indexOf(fname_def) !== -1
|
||||
);
|
||||
const enclosing_defs = [];
|
||||
|
||||
for (const def of defun.enclosed) {
|
||||
if (
|
||||
@@ -528,93 +549,107 @@ function handle_defined_after_hoist(parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// defun is hoisted, so always safe
|
||||
symbols_of_interest.add(def.id);
|
||||
|
||||
// found a reference to another function
|
||||
if (
|
||||
def.assignments === 0
|
||||
&& def.orig.length === 1
|
||||
&& def.orig[0] instanceof AST_SymbolDefun
|
||||
) {
|
||||
defuns_of_interest.add(def.id);
|
||||
symbols_of_interest.add(def.id);
|
||||
|
||||
defuns_of_interest.add(fname_def.id);
|
||||
symbols_of_interest.add(fname_def.id);
|
||||
|
||||
if (!defun_dependencies_map.has(fname_def.id)) {
|
||||
defun_dependencies_map.set(fname_def.id, []);
|
||||
}
|
||||
defun_dependencies_map.get(fname_def.id).push(def.id);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (found_self_ref_in_other_defuns) {
|
||||
def.fixed = false;
|
||||
continue;
|
||||
}
|
||||
enclosing_defs.push(def);
|
||||
}
|
||||
|
||||
// for the slower checks below this loop
|
||||
potential_conflicts.push({ defun, def, fname_def });
|
||||
symbols_of_interest.add(def.id);
|
||||
if (enclosing_defs.length) {
|
||||
dependencies_map.set(fname_def.id, enclosing_defs);
|
||||
defuns_of_interest.add(fname_def.id);
|
||||
symbols_of_interest.add(fname_def.id);
|
||||
defuns_of_interest.add(defun);
|
||||
}
|
||||
}
|
||||
|
||||
// linearize all symbols, and locate defs that are read after the defun
|
||||
if (potential_conflicts.length) {
|
||||
// All "symbols of interest", that is, defuns or defs, that we found.
|
||||
// These are placed in order so we can check which is after which.
|
||||
const found_symbols = [];
|
||||
// Indices of `found_symbols` which are writes
|
||||
const found_symbol_writes = new Set();
|
||||
// Defun ranges are recorded because we don't care if a function uses the def internally
|
||||
const defun_ranges = new Map();
|
||||
// No defuns use outside constants
|
||||
if (!dependencies_map.size) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tw;
|
||||
parent.walk((tw = new TreeWalker((node, descend) => {
|
||||
if (node instanceof AST_Defun && defuns_of_interest.has(node)) {
|
||||
const start = found_symbols.length;
|
||||
descend();
|
||||
const end = found_symbols.length;
|
||||
// Increment to count "symbols of interest" (defuns or defs) that we found.
|
||||
// These are tracked in AST order so we can check which is after which.
|
||||
let symbol_index = 1;
|
||||
// Map a defun ID to its first read (a `symbol_index`)
|
||||
const defun_first_read_map = new Map();
|
||||
// Map a symbol ID to its last write (a `symbol_index`)
|
||||
const symbol_last_write_map = new Map();
|
||||
|
||||
defun_ranges.set(node, { start, end });
|
||||
return true;
|
||||
}
|
||||
// if we found a defun on the list, mark IN_DEFUN=id and descend
|
||||
walk_parent(parent, (node, walk_info) => {
|
||||
if (node instanceof AST_Symbol && node.thedef) {
|
||||
const id = node.definition().id;
|
||||
|
||||
if (node instanceof AST_Symbol && node.thedef) {
|
||||
const id = node.definition().id;
|
||||
if (symbols_of_interest.has(id)) {
|
||||
if (node instanceof AST_SymbolDeclaration || is_lhs(node, tw)) {
|
||||
found_symbol_writes.add(found_symbols.length);
|
||||
}
|
||||
found_symbols.push(id);
|
||||
symbol_index++;
|
||||
|
||||
// Track last-writes to symbols
|
||||
if (symbols_of_interest.has(id)) {
|
||||
if (node instanceof AST_SymbolDeclaration || is_lhs(node, walk_info.parent())) {
|
||||
symbol_last_write_map.set(id, symbol_index);
|
||||
}
|
||||
}
|
||||
})));
|
||||
|
||||
for (const { def, defun, fname_def } of potential_conflicts) {
|
||||
const defun_range = defun_ranges.get(defun);
|
||||
|
||||
// find the index in `found_symbols`, with some special rules:
|
||||
const find = (sym_id, starting_at = 0, must_be_write = false) => {
|
||||
let index = starting_at;
|
||||
|
||||
for (;;) {
|
||||
index = found_symbols.indexOf(sym_id, index);
|
||||
|
||||
if (index === -1) {
|
||||
break;
|
||||
} else if (index >= defun_range.start && index < defun_range.end) {
|
||||
index = defun_range.end;
|
||||
continue;
|
||||
} else if (must_be_write && !found_symbol_writes.has(index)) {
|
||||
index++;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
// Track first-reads of defuns (refined later)
|
||||
if (defuns_of_interest.has(id)) {
|
||||
if (!defun_first_read_map.has(id) && !is_recursive_ref(walk_info, id)) {
|
||||
defun_first_read_map.set(id, symbol_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return index;
|
||||
};
|
||||
// Refine `defun_first_read_map` to be as high as possible
|
||||
for (const [defun, defun_first_read] of defun_first_read_map) {
|
||||
// Update all depdencies of `defun`
|
||||
const queue = new Set(defun_dependencies_map.get(defun));
|
||||
for (const enclosed_defun of queue) {
|
||||
let enclosed_defun_first_read = defun_first_read_map.get(enclosed_defun);
|
||||
if (enclosed_defun_first_read != null && enclosed_defun_first_read < defun_first_read) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const read_defun_at = find(fname_def.id);
|
||||
const wrote_def_at = find(def.id, read_defun_at + 1, true);
|
||||
defun_first_read_map.set(enclosed_defun, defun_first_read);
|
||||
|
||||
const wrote_def_after_reading_defun = read_defun_at != -1 && wrote_def_at != -1 && wrote_def_at > read_defun_at;
|
||||
for (const enclosed_enclosed_defun of defun_dependencies_map.get(enclosed_defun) || []) {
|
||||
queue.add(enclosed_enclosed_defun);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wrote_def_after_reading_defun) {
|
||||
// ensure write-then-read order, otherwise clear `fixed`
|
||||
// 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.
|
||||
for (const [defun, defs] of dependencies_map) {
|
||||
const defun_first_read = defun_first_read_map.get(defun);
|
||||
if (defun_first_read === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const def of defs) {
|
||||
if (def.fixed === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let def_last_write = symbol_last_write_map.get(def.id) || 0;
|
||||
|
||||
if (defun_first_read < def_last_write) {
|
||||
def.fixed = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user