Updated the files.
This commit is contained in:
parent
1553e6b971
commit
753967d4f5
23418 changed files with 3784666 additions and 0 deletions
348
my-app/node_modules/terser/lib/compress/common.js
generated
vendored
Executable file
348
my-app/node_modules/terser/lib/compress/common.js
generated
vendored
Executable file
|
@ -0,0 +1,348 @@
|
|||
/***********************************************************************
|
||||
|
||||
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||
https://github.com/mishoo/UglifyJS2
|
||||
|
||||
-------------------------------- (C) ---------------------------------
|
||||
|
||||
Author: Mihai Bazon
|
||||
<mihai.bazon@gmail.com>
|
||||
http://mihai.bazon.net/blog
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
import {
|
||||
AST_Array,
|
||||
AST_Arrow,
|
||||
AST_BlockStatement,
|
||||
AST_Call,
|
||||
AST_Chain,
|
||||
AST_Class,
|
||||
AST_Const,
|
||||
AST_Constant,
|
||||
AST_DefClass,
|
||||
AST_Defun,
|
||||
AST_EmptyStatement,
|
||||
AST_Export,
|
||||
AST_False,
|
||||
AST_Function,
|
||||
AST_Import,
|
||||
AST_Infinity,
|
||||
AST_LabeledStatement,
|
||||
AST_Lambda,
|
||||
AST_Let,
|
||||
AST_LoopControl,
|
||||
AST_NaN,
|
||||
AST_Node,
|
||||
AST_Null,
|
||||
AST_Number,
|
||||
AST_Object,
|
||||
AST_ObjectKeyVal,
|
||||
AST_PropAccess,
|
||||
AST_RegExp,
|
||||
AST_Scope,
|
||||
AST_Sequence,
|
||||
AST_SimpleStatement,
|
||||
AST_Statement,
|
||||
AST_String,
|
||||
AST_SymbolRef,
|
||||
AST_True,
|
||||
AST_UnaryPrefix,
|
||||
AST_Undefined,
|
||||
|
||||
TreeWalker,
|
||||
walk,
|
||||
walk_abort,
|
||||
walk_parent,
|
||||
} from "../ast.js";
|
||||
import { make_node, regexp_source_fix, string_template, makePredicate } from "../utils/index.js";
|
||||
import { first_in_statement } from "../utils/first_in_statement.js";
|
||||
import { has_flag, TOP } from "./compressor-flags.js";
|
||||
|
||||
export function merge_sequence(array, node) {
|
||||
if (node instanceof AST_Sequence) {
|
||||
array.push(...node.expressions);
|
||||
} else {
|
||||
array.push(node);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
export function make_sequence(orig, expressions) {
|
||||
if (expressions.length == 1) return expressions[0];
|
||||
if (expressions.length == 0) throw new Error("trying to create a sequence with length zero!");
|
||||
return make_node(AST_Sequence, orig, {
|
||||
expressions: expressions.reduce(merge_sequence, [])
|
||||
});
|
||||
}
|
||||
|
||||
export function make_node_from_constant(val, orig) {
|
||||
switch (typeof val) {
|
||||
case "string":
|
||||
return make_node(AST_String, orig, {
|
||||
value: val
|
||||
});
|
||||
case "number":
|
||||
if (isNaN(val)) return make_node(AST_NaN, orig);
|
||||
if (isFinite(val)) {
|
||||
return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
|
||||
operator: "-",
|
||||
expression: make_node(AST_Number, orig, { value: -val })
|
||||
}) : make_node(AST_Number, orig, { value: val });
|
||||
}
|
||||
return val < 0 ? make_node(AST_UnaryPrefix, orig, {
|
||||
operator: "-",
|
||||
expression: make_node(AST_Infinity, orig)
|
||||
}) : make_node(AST_Infinity, orig);
|
||||
case "boolean":
|
||||
return make_node(val ? AST_True : AST_False, orig);
|
||||
case "undefined":
|
||||
return make_node(AST_Undefined, orig);
|
||||
default:
|
||||
if (val === null) {
|
||||
return make_node(AST_Null, orig, { value: null });
|
||||
}
|
||||
if (val instanceof RegExp) {
|
||||
return make_node(AST_RegExp, orig, {
|
||||
value: {
|
||||
source: regexp_source_fix(val.source),
|
||||
flags: val.flags
|
||||
}
|
||||
});
|
||||
}
|
||||
throw new Error(string_template("Can't handle constant of type: {type}", {
|
||||
type: typeof val
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export function best_of_expression(ast1, ast2) {
|
||||
return ast1.size() > ast2.size() ? ast2 : ast1;
|
||||
}
|
||||
|
||||
export function best_of_statement(ast1, ast2) {
|
||||
return best_of_expression(
|
||||
make_node(AST_SimpleStatement, ast1, {
|
||||
body: ast1
|
||||
}),
|
||||
make_node(AST_SimpleStatement, ast2, {
|
||||
body: ast2
|
||||
})
|
||||
).body;
|
||||
}
|
||||
|
||||
/** Find which node is smaller, and return that */
|
||||
export function best_of(compressor, ast1, ast2) {
|
||||
if (first_in_statement(compressor)) {
|
||||
return best_of_statement(ast1, ast2);
|
||||
} else {
|
||||
return best_of_expression(ast1, ast2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Simplify an object property's key, if possible */
|
||||
export function get_simple_key(key) {
|
||||
if (key instanceof AST_Constant) {
|
||||
return key.getValue();
|
||||
}
|
||||
if (key instanceof AST_UnaryPrefix
|
||||
&& key.operator == "void"
|
||||
&& key.expression instanceof AST_Constant) {
|
||||
return;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
export function read_property(obj, key) {
|
||||
key = get_simple_key(key);
|
||||
if (key instanceof AST_Node) return;
|
||||
|
||||
var value;
|
||||
if (obj instanceof AST_Array) {
|
||||
var elements = obj.elements;
|
||||
if (key == "length") return make_node_from_constant(elements.length, obj);
|
||||
if (typeof key == "number" && key in elements) value = elements[key];
|
||||
} else if (obj instanceof AST_Object) {
|
||||
key = "" + key;
|
||||
var props = obj.properties;
|
||||
for (var i = props.length; --i >= 0;) {
|
||||
var prop = props[i];
|
||||
if (!(prop instanceof AST_ObjectKeyVal)) return;
|
||||
if (!value && props[i].key === key) value = props[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return value instanceof AST_SymbolRef && value.fixed_value() || value;
|
||||
}
|
||||
|
||||
export function has_break_or_continue(loop, parent) {
|
||||
var found = false;
|
||||
var tw = new TreeWalker(function(node) {
|
||||
if (found || node instanceof AST_Scope) return true;
|
||||
if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === loop) {
|
||||
return found = true;
|
||||
}
|
||||
});
|
||||
if (parent instanceof AST_LabeledStatement) tw.push(parent);
|
||||
tw.push(loop);
|
||||
loop.body.walk(tw);
|
||||
return found;
|
||||
}
|
||||
|
||||
// we shouldn't compress (1,func)(something) to
|
||||
// func(something) because that changes the meaning of
|
||||
// the func (becomes lexical instead of global).
|
||||
export function maintain_this_binding(parent, orig, val) {
|
||||
if (
|
||||
parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|
||||
|| parent instanceof AST_Call && parent.expression === orig
|
||||
&& (
|
||||
val instanceof AST_Chain
|
||||
|| val instanceof AST_PropAccess
|
||||
|| val instanceof AST_SymbolRef && val.name == "eval"
|
||||
)
|
||||
) {
|
||||
const zero = make_node(AST_Number, orig, { value: 0 });
|
||||
return make_sequence(orig, [ zero, val ]);
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
export function is_func_expr(node) {
|
||||
return node instanceof AST_Arrow || node instanceof AST_Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine whether the node can benefit from negation.
|
||||
* Not the case with arrow functions (you need an extra set of parens). */
|
||||
export function is_iife_call(node) {
|
||||
if (node.TYPE != "Call") return false;
|
||||
return node.expression instanceof AST_Function || is_iife_call(node.expression);
|
||||
}
|
||||
|
||||
export function is_empty(thing) {
|
||||
if (thing === null) return true;
|
||||
if (thing instanceof AST_EmptyStatement) return true;
|
||||
if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
export const identifier_atom = makePredicate("Infinity NaN undefined");
|
||||
export function is_identifier_atom(node) {
|
||||
return node instanceof AST_Infinity
|
||||
|| node instanceof AST_NaN
|
||||
|| node instanceof AST_Undefined;
|
||||
}
|
||||
|
||||
/** Check if this is a SymbolRef node which has one def of a certain AST type */
|
||||
export function is_ref_of(ref, type) {
|
||||
if (!(ref instanceof AST_SymbolRef)) return false;
|
||||
var orig = ref.definition().orig;
|
||||
for (var i = orig.length; --i >= 0;) {
|
||||
if (orig[i] instanceof type) return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**Can we turn { block contents... } into just the block contents ?
|
||||
* Not if one of these is inside.
|
||||
**/
|
||||
export function can_be_evicted_from_block(node) {
|
||||
return !(
|
||||
node instanceof AST_DefClass ||
|
||||
node instanceof AST_Defun ||
|
||||
node instanceof AST_Let ||
|
||||
node instanceof AST_Const ||
|
||||
node instanceof AST_Export ||
|
||||
node instanceof AST_Import
|
||||
);
|
||||
}
|
||||
|
||||
export function as_statement_array(thing) {
|
||||
if (thing === null) return [];
|
||||
if (thing instanceof AST_BlockStatement) return thing.body;
|
||||
if (thing instanceof AST_EmptyStatement) return [];
|
||||
if (thing instanceof AST_Statement) return [ thing ];
|
||||
throw new Error("Can't convert thing to statement array");
|
||||
}
|
||||
|
||||
export function is_reachable(scope_node, defs) {
|
||||
const find_ref = node => {
|
||||
if (node instanceof AST_SymbolRef && defs.includes(node.definition())) {
|
||||
return walk_abort;
|
||||
}
|
||||
};
|
||||
|
||||
return walk_parent(scope_node, (node, info) => {
|
||||
if (node instanceof AST_Scope && node !== scope_node) {
|
||||
var parent = info.parent();
|
||||
|
||||
if (
|
||||
parent instanceof AST_Call
|
||||
&& parent.expression === node
|
||||
// Async/Generators aren't guaranteed to sync evaluate all of
|
||||
// their body steps, so it's possible they close over the variable.
|
||||
&& !(node.async || node.is_generator)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (walk(node, find_ref)) return walk_abort;
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Check if a ref refers to the name of a function/class it's defined within */
|
||||
export function is_recursive_ref(compressor, def) {
|
||||
var node;
|
||||
for (var i = 0; node = compressor.parent(i); i++) {
|
||||
if (node instanceof AST_Lambda || node instanceof AST_Class) {
|
||||
var name = node.name;
|
||||
if (name && name.definition() === def) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO this only works with AST_Defun, shouldn't it work for other ways of defining functions?
|
||||
export function retain_top_func(fn, compressor) {
|
||||
return compressor.top_retain
|
||||
&& fn instanceof AST_Defun
|
||||
&& has_flag(fn, TOP)
|
||||
&& fn.name
|
||||
&& compressor.top_retain(fn.name.definition());
|
||||
}
|
63
my-app/node_modules/terser/lib/compress/compressor-flags.js
generated
vendored
Executable file
63
my-app/node_modules/terser/lib/compress/compressor-flags.js
generated
vendored
Executable file
|
@ -0,0 +1,63 @@
|
|||
/***********************************************************************
|
||||
|
||||
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||
https://github.com/mishoo/UglifyJS2
|
||||
|
||||
-------------------------------- (C) ---------------------------------
|
||||
|
||||
Author: Mihai Bazon
|
||||
<mihai.bazon@gmail.com>
|
||||
http://mihai.bazon.net/blog
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
// bitfield flags to be stored in node.flags.
|
||||
// These are set and unset during compression, and store information in the node without requiring multiple fields.
|
||||
export const UNUSED = 0b00000001;
|
||||
export const TRUTHY = 0b00000010;
|
||||
export const FALSY = 0b00000100;
|
||||
export const UNDEFINED = 0b00001000;
|
||||
export const INLINED = 0b00010000;
|
||||
|
||||
// Nodes to which values are ever written. Used when keep_assign is part of the unused option string.
|
||||
export const WRITE_ONLY = 0b00100000;
|
||||
|
||||
// information specific to a single compression pass
|
||||
export const SQUEEZED = 0b0000000100000000;
|
||||
export const OPTIMIZED = 0b0000001000000000;
|
||||
export const TOP = 0b0000010000000000;
|
||||
export const CLEAR_BETWEEN_PASSES = SQUEEZED | OPTIMIZED | TOP;
|
||||
|
||||
export const has_flag = (node, flag) => node.flags & flag;
|
||||
export const set_flag = (node, flag) => { node.flags |= flag; };
|
||||
export const clear_flag = (node, flag) => { node.flags &= ~flag; };
|
375
my-app/node_modules/terser/lib/compress/drop-side-effect-free.js
generated
vendored
Executable file
375
my-app/node_modules/terser/lib/compress/drop-side-effect-free.js
generated
vendored
Executable file
|
@ -0,0 +1,375 @@
|
|||
/***********************************************************************
|
||||
|
||||
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||
https://github.com/mishoo/UglifyJS2
|
||||
|
||||
-------------------------------- (C) ---------------------------------
|
||||
|
||||
Author: Mihai Bazon
|
||||
<mihai.bazon@gmail.com>
|
||||
http://mihai.bazon.net/blog
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
import {
|
||||
AST_Accessor,
|
||||
AST_Array,
|
||||
AST_Arrow,
|
||||
AST_Assign,
|
||||
AST_Binary,
|
||||
AST_Call,
|
||||
AST_Chain,
|
||||
AST_Class,
|
||||
AST_ClassStaticBlock,
|
||||
AST_ClassProperty,
|
||||
AST_ConciseMethod,
|
||||
AST_Conditional,
|
||||
AST_Constant,
|
||||
AST_DefClass,
|
||||
AST_Dot,
|
||||
AST_Expansion,
|
||||
AST_Function,
|
||||
AST_Node,
|
||||
AST_Number,
|
||||
AST_Object,
|
||||
AST_ObjectGetter,
|
||||
AST_ObjectKeyVal,
|
||||
AST_ObjectProperty,
|
||||
AST_ObjectSetter,
|
||||
AST_PropAccess,
|
||||
AST_Scope,
|
||||
AST_Sequence,
|
||||
AST_SimpleStatement,
|
||||
AST_Sub,
|
||||
AST_SymbolRef,
|
||||
AST_TemplateSegment,
|
||||
AST_TemplateString,
|
||||
AST_This,
|
||||
AST_Unary,
|
||||
} from "../ast.js";
|
||||
import { make_node, return_null, return_this } from "../utils/index.js";
|
||||
import { first_in_statement } from "../utils/first_in_statement.js";
|
||||
|
||||
import { pure_prop_access_globals } from "./native-objects.js";
|
||||
import { lazy_op, unary_side_effects, is_nullish_shortcircuited } from "./inference.js";
|
||||
import { WRITE_ONLY, set_flag, clear_flag } from "./compressor-flags.js";
|
||||
import { make_sequence, is_func_expr, is_iife_call } from "./common.js";
|
||||
|
||||
// AST_Node#drop_side_effect_free() gets called when we don't care about the value,
|
||||
// only about side effects. We'll be defining this method for each node type in this module
|
||||
//
|
||||
// Examples:
|
||||
// foo++ -> foo++
|
||||
// 1 + func() -> func()
|
||||
// 10 -> (nothing)
|
||||
// knownPureFunc(foo++) -> foo++
|
||||
|
||||
function def_drop_side_effect_free(node, func) {
|
||||
node.DEFMETHOD("drop_side_effect_free", func);
|
||||
}
|
||||
|
||||
// Drop side-effect-free elements from an array of expressions.
|
||||
// Returns an array of expressions with side-effects or null
|
||||
// if all elements were dropped. Note: original array may be
|
||||
// returned if nothing changed.
|
||||
function trim(nodes, compressor, first_in_statement) {
|
||||
var len = nodes.length;
|
||||
if (!len) return null;
|
||||
|
||||
var ret = [], changed = false;
|
||||
for (var i = 0; i < len; i++) {
|
||||
var node = nodes[i].drop_side_effect_free(compressor, first_in_statement);
|
||||
changed |= node !== nodes[i];
|
||||
if (node) {
|
||||
ret.push(node);
|
||||
first_in_statement = false;
|
||||
}
|
||||
}
|
||||
return changed ? ret.length ? ret : null : nodes;
|
||||
}
|
||||
|
||||
def_drop_side_effect_free(AST_Node, return_this);
|
||||
def_drop_side_effect_free(AST_Constant, return_null);
|
||||
def_drop_side_effect_free(AST_This, return_null);
|
||||
|
||||
def_drop_side_effect_free(AST_Call, function (compressor, first_in_statement) {
|
||||
if (is_nullish_shortcircuited(this, compressor)) {
|
||||
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||
}
|
||||
|
||||
if (!this.is_callee_pure(compressor)) {
|
||||
if (this.expression.is_call_pure(compressor)) {
|
||||
var exprs = this.args.slice();
|
||||
exprs.unshift(this.expression.expression);
|
||||
exprs = trim(exprs, compressor, first_in_statement);
|
||||
return exprs && make_sequence(this, exprs);
|
||||
}
|
||||
if (is_func_expr(this.expression)
|
||||
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
|
||||
var node = this.clone();
|
||||
node.expression.process_expression(false, compressor);
|
||||
return node;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
var args = trim(this.args, compressor, first_in_statement);
|
||||
return args && make_sequence(this, args);
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Accessor, return_null);
|
||||
|
||||
def_drop_side_effect_free(AST_Function, return_null);
|
||||
|
||||
def_drop_side_effect_free(AST_Arrow, return_null);
|
||||
|
||||
def_drop_side_effect_free(AST_Class, function (compressor) {
|
||||
const with_effects = [];
|
||||
const trimmed_extends = this.extends && this.extends.drop_side_effect_free(compressor);
|
||||
if (trimmed_extends)
|
||||
with_effects.push(trimmed_extends);
|
||||
|
||||
for (const prop of this.properties) {
|
||||
if (prop instanceof AST_ClassStaticBlock) {
|
||||
if (prop.has_side_effects(compressor)) {
|
||||
return this; // Be cautious about these
|
||||
}
|
||||
} else {
|
||||
const trimmed_prop = prop.drop_side_effect_free(compressor);
|
||||
if (trimmed_prop) {
|
||||
if (trimmed_prop.contains_this()) return this;
|
||||
|
||||
with_effects.push(trimmed_prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!with_effects.length)
|
||||
return null;
|
||||
|
||||
const exprs = make_sequence(this, with_effects);
|
||||
if (this instanceof AST_DefClass) {
|
||||
// We want a statement
|
||||
return make_node(AST_SimpleStatement, this, { body: exprs });
|
||||
} else {
|
||||
return exprs;
|
||||
}
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_ClassProperty, function (compressor) {
|
||||
const key = this.computed_key() && this.key.drop_side_effect_free(compressor);
|
||||
|
||||
const value = this.static && this.value
|
||||
&& this.value.drop_side_effect_free(compressor);
|
||||
|
||||
if (key && value)
|
||||
return make_sequence(this, [key, value]);
|
||||
return key || value || null;
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Binary, function (compressor, first_in_statement) {
|
||||
var right = this.right.drop_side_effect_free(compressor);
|
||||
if (!right)
|
||||
return this.left.drop_side_effect_free(compressor, first_in_statement);
|
||||
if (lazy_op.has(this.operator)) {
|
||||
if (right === this.right)
|
||||
return this;
|
||||
var node = this.clone();
|
||||
node.right = right;
|
||||
return node;
|
||||
} else {
|
||||
var left = this.left.drop_side_effect_free(compressor, first_in_statement);
|
||||
if (!left)
|
||||
return this.right.drop_side_effect_free(compressor, first_in_statement);
|
||||
return make_sequence(this, [left, right]);
|
||||
}
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Assign, function (compressor) {
|
||||
if (this.logical)
|
||||
return this;
|
||||
|
||||
var left = this.left;
|
||||
if (left.has_side_effects(compressor)
|
||||
|| compressor.has_directive("use strict")
|
||||
&& left instanceof AST_PropAccess
|
||||
&& left.expression.is_constant()) {
|
||||
return this;
|
||||
}
|
||||
set_flag(this, WRITE_ONLY);
|
||||
while (left instanceof AST_PropAccess) {
|
||||
left = left.expression;
|
||||
}
|
||||
if (left.is_constant_expression(compressor.find_parent(AST_Scope))) {
|
||||
return this.right.drop_side_effect_free(compressor);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Conditional, function (compressor) {
|
||||
var consequent = this.consequent.drop_side_effect_free(compressor);
|
||||
var alternative = this.alternative.drop_side_effect_free(compressor);
|
||||
if (consequent === this.consequent && alternative === this.alternative)
|
||||
return this;
|
||||
if (!consequent)
|
||||
return alternative ? make_node(AST_Binary, this, {
|
||||
operator: "||",
|
||||
left: this.condition,
|
||||
right: alternative
|
||||
}) : this.condition.drop_side_effect_free(compressor);
|
||||
if (!alternative)
|
||||
return make_node(AST_Binary, this, {
|
||||
operator: "&&",
|
||||
left: this.condition,
|
||||
right: consequent
|
||||
});
|
||||
var node = this.clone();
|
||||
node.consequent = consequent;
|
||||
node.alternative = alternative;
|
||||
return node;
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Unary, function (compressor, first_in_statement) {
|
||||
if (unary_side_effects.has(this.operator)) {
|
||||
if (!this.expression.has_side_effects(compressor)) {
|
||||
set_flag(this, WRITE_ONLY);
|
||||
} else {
|
||||
clear_flag(this, WRITE_ONLY);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef)
|
||||
return null;
|
||||
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||
if (first_in_statement && expression && is_iife_call(expression)) {
|
||||
if (expression === this.expression && this.operator == "!")
|
||||
return this;
|
||||
return expression.negate(compressor, first_in_statement);
|
||||
}
|
||||
return expression;
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_SymbolRef, function (compressor) {
|
||||
const safe_access = this.is_declared(compressor)
|
||||
|| pure_prop_access_globals.has(this.name);
|
||||
return safe_access ? null : this;
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Object, function (compressor, first_in_statement) {
|
||||
var values = trim(this.properties, compressor, first_in_statement);
|
||||
return values && make_sequence(this, values);
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_ObjectProperty, function (compressor, first_in_statement) {
|
||||
const computed_key = this instanceof AST_ObjectKeyVal && this.key instanceof AST_Node;
|
||||
const key = computed_key && this.key.drop_side_effect_free(compressor, first_in_statement);
|
||||
const value = this.value && this.value.drop_side_effect_free(compressor, first_in_statement);
|
||||
if (key && value) {
|
||||
return make_sequence(this, [key, value]);
|
||||
}
|
||||
return key || value;
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_ConciseMethod, function () {
|
||||
return this.computed_key() ? this.key : null;
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_ObjectGetter, function () {
|
||||
return this.computed_key() ? this.key : null;
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_ObjectSetter, function () {
|
||||
return this.computed_key() ? this.key : null;
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Array, function (compressor, first_in_statement) {
|
||||
var values = trim(this.elements, compressor, first_in_statement);
|
||||
return values && make_sequence(this, values);
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Dot, function (compressor, first_in_statement) {
|
||||
if (is_nullish_shortcircuited(this, compressor)) {
|
||||
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||
}
|
||||
if (!this.optional && this.expression.may_throw_on_access(compressor)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Sub, function (compressor, first_in_statement) {
|
||||
if (is_nullish_shortcircuited(this, compressor)) {
|
||||
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||
}
|
||||
if (!this.optional && this.expression.may_throw_on_access(compressor)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var property = this.property.drop_side_effect_free(compressor);
|
||||
if (property && this.optional) return this;
|
||||
|
||||
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||
|
||||
if (expression && property) return make_sequence(this, [expression, property]);
|
||||
return expression || property;
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Chain, function (compressor, first_in_statement) {
|
||||
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Sequence, function (compressor) {
|
||||
var last = this.tail_node();
|
||||
var expr = last.drop_side_effect_free(compressor);
|
||||
if (expr === last)
|
||||
return this;
|
||||
var expressions = this.expressions.slice(0, -1);
|
||||
if (expr)
|
||||
expressions.push(expr);
|
||||
if (!expressions.length) {
|
||||
return make_node(AST_Number, this, { value: 0 });
|
||||
}
|
||||
return make_sequence(this, expressions);
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_Expansion, function (compressor, first_in_statement) {
|
||||
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||
});
|
||||
|
||||
def_drop_side_effect_free(AST_TemplateSegment, return_null);
|
||||
|
||||
def_drop_side_effect_free(AST_TemplateString, function (compressor) {
|
||||
var values = trim(this.segments, compressor, first_in_statement);
|
||||
return values && make_sequence(this, values);
|
||||
});
|
482
my-app/node_modules/terser/lib/compress/drop-unused.js
generated
vendored
Executable file
482
my-app/node_modules/terser/lib/compress/drop-unused.js
generated
vendored
Executable file
|
@ -0,0 +1,482 @@
|
|||
/***********************************************************************
|
||||
|
||||
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||
https://github.com/mishoo/UglifyJS2
|
||||
|
||||
-------------------------------- (C) ---------------------------------
|
||||
|
||||
Author: Mihai Bazon
|
||||
<mihai.bazon@gmail.com>
|
||||
http://mihai.bazon.net/blog
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
import {
|
||||
AST_Accessor,
|
||||
AST_Assign,
|
||||
AST_BlockStatement,
|
||||
AST_Class,
|
||||
AST_ClassExpression,
|
||||
AST_ClassStaticBlock,
|
||||
AST_DefaultAssign,
|
||||
AST_DefClass,
|
||||
AST_Definitions,
|
||||
AST_Defun,
|
||||
AST_Destructuring,
|
||||
AST_EmptyStatement,
|
||||
AST_Expansion,
|
||||
AST_Export,
|
||||
AST_For,
|
||||
AST_ForIn,
|
||||
AST_Function,
|
||||
AST_LabeledStatement,
|
||||
AST_Lambda,
|
||||
AST_Number,
|
||||
AST_Scope,
|
||||
AST_SimpleStatement,
|
||||
AST_SymbolBlockDeclaration,
|
||||
AST_SymbolCatch,
|
||||
AST_SymbolDeclaration,
|
||||
AST_SymbolFunarg,
|
||||
AST_SymbolRef,
|
||||
AST_SymbolVar,
|
||||
AST_Toplevel,
|
||||
AST_Unary,
|
||||
AST_Var,
|
||||
|
||||
TreeTransformer,
|
||||
TreeWalker,
|
||||
walk,
|
||||
} from "../ast.js";
|
||||
import {
|
||||
keep_name,
|
||||
make_node,
|
||||
map_add,
|
||||
MAP,
|
||||
remove,
|
||||
return_false,
|
||||
} from "../utils/index.js";
|
||||
import { SymbolDef } from "../scope.js";
|
||||
|
||||
import {
|
||||
WRITE_ONLY,
|
||||
UNUSED,
|
||||
|
||||
has_flag,
|
||||
set_flag,
|
||||
} from "./compressor-flags.js";
|
||||
import {
|
||||
make_sequence,
|
||||
maintain_this_binding,
|
||||
is_empty,
|
||||
is_ref_of,
|
||||
can_be_evicted_from_block,
|
||||
} from "./common.js";
|
||||
|
||||
const r_keep_assign = /keep_assign/;
|
||||
|
||||
/** Drop unused variables from this scope */
|
||||
AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
||||
if (!compressor.option("unused")) return;
|
||||
if (compressor.has_directive("use asm")) return;
|
||||
if (!this.variables) return; // not really a scope (eg: AST_Class)
|
||||
|
||||
var self = this;
|
||||
if (self.pinned()) return;
|
||||
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
|
||||
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
|
||||
const assign_as_unused = r_keep_assign.test(compressor.option("unused")) ? return_false : function(node) {
|
||||
if (node instanceof AST_Assign
|
||||
&& !node.logical
|
||||
&& (has_flag(node, WRITE_ONLY) || node.operator == "=")
|
||||
) {
|
||||
return node.left;
|
||||
}
|
||||
if (node instanceof AST_Unary && has_flag(node, WRITE_ONLY)) {
|
||||
return node.expression;
|
||||
}
|
||||
};
|
||||
var in_use_ids = new Map();
|
||||
var fixed_ids = new Map();
|
||||
if (self instanceof AST_Toplevel && compressor.top_retain) {
|
||||
self.variables.forEach(function(def) {
|
||||
if (compressor.top_retain(def)) {
|
||||
in_use_ids.set(def.id, def);
|
||||
}
|
||||
});
|
||||
}
|
||||
var var_defs_by_id = new Map();
|
||||
var initializations = new Map();
|
||||
// pass 1: find out which symbols are directly used in
|
||||
// this scope (not in nested scopes).
|
||||
var scope = this;
|
||||
var tw = new TreeWalker(function(node, descend) {
|
||||
if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) {
|
||||
node.argnames.forEach(function(argname) {
|
||||
if (!(argname instanceof AST_SymbolDeclaration)) return;
|
||||
var def = argname.definition();
|
||||
in_use_ids.set(def.id, def);
|
||||
});
|
||||
}
|
||||
if (node === self) return;
|
||||
if (node instanceof AST_Class) {
|
||||
if (node.has_side_effects(compressor)) {
|
||||
node.visit_nondeferred_class_parts(tw);
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_Defun || node instanceof AST_DefClass) {
|
||||
var node_def = node.name.definition();
|
||||
const in_export = tw.parent() instanceof AST_Export;
|
||||
if (in_export || !drop_funcs && scope === self) {
|
||||
if (node_def.global) {
|
||||
in_use_ids.set(node_def.id, node_def);
|
||||
}
|
||||
}
|
||||
|
||||
map_add(initializations, node_def.id, node);
|
||||
return true; // don't go in nested scopes
|
||||
}
|
||||
// In the root scope, we drop things. In inner scopes, we just check for uses.
|
||||
const in_root_scope = scope === self;
|
||||
if (node instanceof AST_SymbolFunarg && in_root_scope) {
|
||||
map_add(var_defs_by_id, node.definition().id, node);
|
||||
}
|
||||
if (node instanceof AST_Definitions && in_root_scope) {
|
||||
const in_export = tw.parent() instanceof AST_Export;
|
||||
node.definitions.forEach(function(def) {
|
||||
if (def.name instanceof AST_SymbolVar) {
|
||||
map_add(var_defs_by_id, def.name.definition().id, def);
|
||||
}
|
||||
if (in_export || !drop_vars) {
|
||||
walk(def.name, node => {
|
||||
if (node instanceof AST_SymbolDeclaration) {
|
||||
const def = node.definition();
|
||||
if (def.global) {
|
||||
in_use_ids.set(def.id, def);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (def.name instanceof AST_Destructuring) {
|
||||
def.walk(tw);
|
||||
}
|
||||
if (def.name instanceof AST_SymbolDeclaration && def.value) {
|
||||
var node_def = def.name.definition();
|
||||
map_add(initializations, node_def.id, def.value);
|
||||
if (!node_def.chained && def.name.fixed_value() === def.value) {
|
||||
fixed_ids.set(node_def.id, def);
|
||||
}
|
||||
if (def.value.has_side_effects(compressor)) {
|
||||
def.value.walk(tw);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return scan_ref_scoped(node, descend);
|
||||
});
|
||||
self.walk(tw);
|
||||
// pass 2: for every used symbol we need to walk its
|
||||
// initialization code to figure out if it uses other
|
||||
// symbols (that may not be in_use).
|
||||
tw = new TreeWalker(scan_ref_scoped);
|
||||
in_use_ids.forEach(function (def) {
|
||||
var init = initializations.get(def.id);
|
||||
if (init) init.forEach(function(init) {
|
||||
init.walk(tw);
|
||||
});
|
||||
});
|
||||
// pass 3: we should drop declarations not in_use
|
||||
var tt = new TreeTransformer(
|
||||
function before(node, descend, in_list) {
|
||||
var parent = tt.parent();
|
||||
if (drop_vars) {
|
||||
const sym = assign_as_unused(node);
|
||||
if (sym instanceof AST_SymbolRef) {
|
||||
var def = sym.definition();
|
||||
var in_use = in_use_ids.has(def.id);
|
||||
if (node instanceof AST_Assign) {
|
||||
if (!in_use || fixed_ids.has(def.id) && fixed_ids.get(def.id) !== node) {
|
||||
return maintain_this_binding(parent, node, node.right.transform(tt));
|
||||
}
|
||||
} else if (!in_use) {
|
||||
return in_list ? MAP.skip : make_node(AST_Number, node, { value: 0 });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scope !== self) return;
|
||||
var def;
|
||||
if (node.name
|
||||
&& (node instanceof AST_ClassExpression
|
||||
&& !keep_name(compressor.option("keep_classnames"), (def = node.name.definition()).name)
|
||||
|| node instanceof AST_Function
|
||||
&& !keep_name(compressor.option("keep_fnames"), (def = node.name.definition()).name))) {
|
||||
// any declarations with same name will overshadow
|
||||
// name of this anonymous function and can therefore
|
||||
// never be used anywhere
|
||||
if (!in_use_ids.has(def.id) || def.orig.length > 1) node.name = null;
|
||||
}
|
||||
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
||||
var trim = !compressor.option("keep_fargs");
|
||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||
var sym = a[i];
|
||||
if (sym instanceof AST_Expansion) {
|
||||
sym = sym.expression;
|
||||
}
|
||||
if (sym instanceof AST_DefaultAssign) {
|
||||
sym = sym.left;
|
||||
}
|
||||
// Do not drop destructuring arguments.
|
||||
// They constitute a type assertion of sorts
|
||||
if (
|
||||
!(sym instanceof AST_Destructuring)
|
||||
&& !in_use_ids.has(sym.definition().id)
|
||||
) {
|
||||
set_flag(sym, UNUSED);
|
||||
if (trim) {
|
||||
a.pop();
|
||||
}
|
||||
} else {
|
||||
trim = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_DefClass && node !== self) {
|
||||
const def = node.name.definition();
|
||||
descend(node, this);
|
||||
const keep_class = def.global && !drop_funcs || in_use_ids.has(def.id);
|
||||
if (!keep_class) {
|
||||
const kept = node.drop_side_effect_free(compressor);
|
||||
if (kept == null) {
|
||||
def.eliminated++;
|
||||
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
return kept;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
if (node instanceof AST_Defun && node !== self) {
|
||||
const def = node.name.definition();
|
||||
const keep = def.global && !drop_funcs || in_use_ids.has(def.id);
|
||||
if (!keep) {
|
||||
def.eliminated++;
|
||||
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
|
||||
var drop_block = !(parent instanceof AST_Toplevel) && !(node instanceof AST_Var);
|
||||
// place uninitialized names at the start
|
||||
var body = [], head = [], tail = [];
|
||||
// for unused names whose initialization has
|
||||
// side effects, we can cascade the init. code
|
||||
// into the next one, or next statement.
|
||||
var side_effects = [];
|
||||
node.definitions.forEach(function(def) {
|
||||
if (def.value) def.value = def.value.transform(tt);
|
||||
var is_destructure = def.name instanceof AST_Destructuring;
|
||||
var sym = is_destructure
|
||||
? new SymbolDef(null, { name: "<destructure>" }) /* fake SymbolDef */
|
||||
: def.name.definition();
|
||||
if (drop_block && sym.global) return tail.push(def);
|
||||
if (!(drop_vars || drop_block)
|
||||
|| is_destructure
|
||||
&& (def.name.names.length
|
||||
|| def.name.is_array
|
||||
|| compressor.option("pure_getters") != true)
|
||||
|| in_use_ids.has(sym.id)
|
||||
) {
|
||||
if (def.value && fixed_ids.has(sym.id) && fixed_ids.get(sym.id) !== def) {
|
||||
def.value = def.value.drop_side_effect_free(compressor);
|
||||
}
|
||||
if (def.name instanceof AST_SymbolVar) {
|
||||
var var_defs = var_defs_by_id.get(sym.id);
|
||||
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
|
||||
if (def.value) {
|
||||
var ref = make_node(AST_SymbolRef, def.name, def.name);
|
||||
sym.references.push(ref);
|
||||
var assign = make_node(AST_Assign, def, {
|
||||
operator: "=",
|
||||
logical: false,
|
||||
left: ref,
|
||||
right: def.value
|
||||
});
|
||||
if (fixed_ids.get(sym.id) === def) {
|
||||
fixed_ids.set(sym.id, assign);
|
||||
}
|
||||
side_effects.push(assign.transform(tt));
|
||||
}
|
||||
remove(var_defs, def);
|
||||
sym.eliminated++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (def.value) {
|
||||
if (side_effects.length > 0) {
|
||||
if (tail.length > 0) {
|
||||
side_effects.push(def.value);
|
||||
def.value = make_sequence(def.value, side_effects);
|
||||
} else {
|
||||
body.push(make_node(AST_SimpleStatement, node, {
|
||||
body: make_sequence(node, side_effects)
|
||||
}));
|
||||
}
|
||||
side_effects = [];
|
||||
}
|
||||
tail.push(def);
|
||||
} else {
|
||||
head.push(def);
|
||||
}
|
||||
} else if (sym.orig[0] instanceof AST_SymbolCatch) {
|
||||
var value = def.value && def.value.drop_side_effect_free(compressor);
|
||||
if (value) side_effects.push(value);
|
||||
def.value = null;
|
||||
head.push(def);
|
||||
} else {
|
||||
var value = def.value && def.value.drop_side_effect_free(compressor);
|
||||
if (value) {
|
||||
side_effects.push(value);
|
||||
}
|
||||
sym.eliminated++;
|
||||
}
|
||||
});
|
||||
if (head.length > 0 || tail.length > 0) {
|
||||
node.definitions = head.concat(tail);
|
||||
body.push(node);
|
||||
}
|
||||
if (side_effects.length > 0) {
|
||||
body.push(make_node(AST_SimpleStatement, node, {
|
||||
body: make_sequence(node, side_effects)
|
||||
}));
|
||||
}
|
||||
switch (body.length) {
|
||||
case 0:
|
||||
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
|
||||
case 1:
|
||||
return body[0];
|
||||
default:
|
||||
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { body });
|
||||
}
|
||||
}
|
||||
// certain combination of unused name + side effect leads to:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/44
|
||||
// https://github.com/mishoo/UglifyJS2/issues/1830
|
||||
// https://github.com/mishoo/UglifyJS2/issues/1838
|
||||
// that's an invalid AST.
|
||||
// We fix it at this stage by moving the `var` outside the `for`.
|
||||
if (node instanceof AST_For) {
|
||||
descend(node, this);
|
||||
var block;
|
||||
if (node.init instanceof AST_BlockStatement) {
|
||||
block = node.init;
|
||||
node.init = block.body.pop();
|
||||
block.body.push(node);
|
||||
}
|
||||
if (node.init instanceof AST_SimpleStatement) {
|
||||
node.init = node.init.body;
|
||||
} else if (is_empty(node.init)) {
|
||||
node.init = null;
|
||||
}
|
||||
return !block ? node : in_list ? MAP.splice(block.body) : block;
|
||||
}
|
||||
if (node instanceof AST_LabeledStatement
|
||||
&& node.body instanceof AST_For
|
||||
) {
|
||||
descend(node, this);
|
||||
if (node.body instanceof AST_BlockStatement) {
|
||||
var block = node.body;
|
||||
node.body = block.body.pop();
|
||||
block.body.push(node);
|
||||
return in_list ? MAP.splice(block.body) : block;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
if (node instanceof AST_BlockStatement) {
|
||||
descend(node, this);
|
||||
if (in_list && node.body.every(can_be_evicted_from_block)) {
|
||||
return MAP.splice(node.body);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
if (node instanceof AST_Scope && !(node instanceof AST_ClassStaticBlock)) {
|
||||
const save_scope = scope;
|
||||
scope = node;
|
||||
descend(node, this);
|
||||
scope = save_scope;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
self.transform(tt);
|
||||
|
||||
function scan_ref_scoped(node, descend) {
|
||||
var node_def;
|
||||
const sym = assign_as_unused(node);
|
||||
if (sym instanceof AST_SymbolRef
|
||||
&& !is_ref_of(node.left, AST_SymbolBlockDeclaration)
|
||||
&& self.variables.get(sym.name) === (node_def = sym.definition())
|
||||
) {
|
||||
if (node instanceof AST_Assign) {
|
||||
node.right.walk(tw);
|
||||
if (!node_def.chained && node.left.fixed_value() === node.right) {
|
||||
fixed_ids.set(node_def.id, node);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
node_def = node.definition();
|
||||
if (!in_use_ids.has(node_def.id)) {
|
||||
in_use_ids.set(node_def.id, node_def);
|
||||
if (node_def.orig[0] instanceof AST_SymbolCatch) {
|
||||
const redef = node_def.scope.is_block_scope()
|
||||
&& node_def.scope.get_defun_scope().variables.get(node_def.name);
|
||||
if (redef) in_use_ids.set(redef.id, redef);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Class) {
|
||||
descend();
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Scope && !(node instanceof AST_ClassStaticBlock)) {
|
||||
var save_scope = scope;
|
||||
scope = node;
|
||||
descend();
|
||||
scope = save_scope;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
488
my-app/node_modules/terser/lib/compress/evaluate.js
generated
vendored
Executable file
488
my-app/node_modules/terser/lib/compress/evaluate.js
generated
vendored
Executable file
|
@ -0,0 +1,488 @@
|
|||
/***********************************************************************
|
||||
|
||||
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||
https://github.com/mishoo/UglifyJS2
|
||||
|
||||
-------------------------------- (C) ---------------------------------
|
||||
|
||||
Author: Mihai Bazon
|
||||
<mihai.bazon@gmail.com>
|
||||
http://mihai.bazon.net/blog
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
import {
|
||||
HOP,
|
||||
makePredicate,
|
||||
return_this,
|
||||
string_template,
|
||||
regexp_source_fix,
|
||||
regexp_is_safe,
|
||||
} from "../utils/index.js";
|
||||
import {
|
||||
AST_Array,
|
||||
AST_BigInt,
|
||||
AST_Binary,
|
||||
AST_Call,
|
||||
AST_Chain,
|
||||
AST_Class,
|
||||
AST_Conditional,
|
||||
AST_Constant,
|
||||
AST_Dot,
|
||||
AST_Expansion,
|
||||
AST_Function,
|
||||
AST_Lambda,
|
||||
AST_New,
|
||||
AST_Node,
|
||||
AST_Object,
|
||||
AST_PropAccess,
|
||||
AST_RegExp,
|
||||
AST_Statement,
|
||||
AST_Symbol,
|
||||
AST_SymbolRef,
|
||||
AST_TemplateString,
|
||||
AST_UnaryPrefix,
|
||||
AST_With,
|
||||
} from "../ast.js";
|
||||
import { is_undeclared_ref} from "./inference.js";
|
||||
import { is_pure_native_value, is_pure_native_fn, is_pure_native_method } from "./native-objects.js";
|
||||
|
||||
// methods to evaluate a constant expression
|
||||
|
||||
function def_eval(node, func) {
|
||||
node.DEFMETHOD("_eval", func);
|
||||
}
|
||||
|
||||
// Used to propagate a nullish short-circuit signal upwards through the chain.
|
||||
export const nullish = Symbol("This AST_Chain is nullish");
|
||||
|
||||
// If the node has been successfully reduced to a constant,
|
||||
// then its value is returned; otherwise the element itself
|
||||
// is returned.
|
||||
// They can be distinguished as constant value is never a
|
||||
// descendant of AST_Node.
|
||||
AST_Node.DEFMETHOD("evaluate", function (compressor) {
|
||||
if (!compressor.option("evaluate"))
|
||||
return this;
|
||||
var val = this._eval(compressor, 1);
|
||||
if (!val || val instanceof RegExp)
|
||||
return val;
|
||||
if (typeof val == "function" || typeof val == "object" || val == nullish)
|
||||
return this;
|
||||
|
||||
// Evaluated strings can be larger than the original expression
|
||||
if (typeof val === "string") {
|
||||
const unevaluated_size = this.size(compressor);
|
||||
if (val.length + 2 > unevaluated_size) return this;
|
||||
}
|
||||
|
||||
return val;
|
||||
});
|
||||
|
||||
var unaryPrefix = makePredicate("! ~ - + void");
|
||||
AST_Node.DEFMETHOD("is_constant", function () {
|
||||
// Accomodate when compress option evaluate=false
|
||||
// as well as the common constant expressions !0 and -1
|
||||
if (this instanceof AST_Constant) {
|
||||
return !(this instanceof AST_RegExp);
|
||||
} else {
|
||||
return this instanceof AST_UnaryPrefix
|
||||
&& this.expression instanceof AST_Constant
|
||||
&& unaryPrefix.has(this.operator);
|
||||
}
|
||||
});
|
||||
|
||||
def_eval(AST_Statement, function () {
|
||||
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
|
||||
});
|
||||
|
||||
def_eval(AST_Lambda, return_this);
|
||||
def_eval(AST_Class, return_this);
|
||||
def_eval(AST_Node, return_this);
|
||||
def_eval(AST_Constant, function () {
|
||||
return this.getValue();
|
||||
});
|
||||
|
||||
def_eval(AST_BigInt, return_this);
|
||||
|
||||
def_eval(AST_RegExp, function (compressor) {
|
||||
let evaluated = compressor.evaluated_regexps.get(this.value);
|
||||
if (evaluated === undefined && regexp_is_safe(this.value.source)) {
|
||||
try {
|
||||
const { source, flags } = this.value;
|
||||
evaluated = new RegExp(source, flags);
|
||||
} catch (e) {
|
||||
evaluated = null;
|
||||
}
|
||||
compressor.evaluated_regexps.set(this.value, evaluated);
|
||||
}
|
||||
return evaluated || this;
|
||||
});
|
||||
|
||||
def_eval(AST_TemplateString, function () {
|
||||
if (this.segments.length !== 1) return this;
|
||||
return this.segments[0].value;
|
||||
});
|
||||
|
||||
def_eval(AST_Function, function (compressor) {
|
||||
if (compressor.option("unsafe")) {
|
||||
var fn = function () { };
|
||||
fn.node = this;
|
||||
fn.toString = () => this.print_to_string();
|
||||
return fn;
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
def_eval(AST_Array, function (compressor, depth) {
|
||||
if (compressor.option("unsafe")) {
|
||||
var elements = [];
|
||||
for (var i = 0, len = this.elements.length; i < len; i++) {
|
||||
var element = this.elements[i];
|
||||
var value = element._eval(compressor, depth);
|
||||
if (element === value)
|
||||
return this;
|
||||
elements.push(value);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
def_eval(AST_Object, function (compressor, depth) {
|
||||
if (compressor.option("unsafe")) {
|
||||
var val = {};
|
||||
for (var i = 0, len = this.properties.length; i < len; i++) {
|
||||
var prop = this.properties[i];
|
||||
if (prop instanceof AST_Expansion)
|
||||
return this;
|
||||
var key = prop.key;
|
||||
if (key instanceof AST_Symbol) {
|
||||
key = key.name;
|
||||
} else if (key instanceof AST_Node) {
|
||||
key = key._eval(compressor, depth);
|
||||
if (key === prop.key)
|
||||
return this;
|
||||
}
|
||||
if (typeof Object.prototype[key] === "function") {
|
||||
return this;
|
||||
}
|
||||
if (prop.value instanceof AST_Function)
|
||||
continue;
|
||||
val[key] = prop.value._eval(compressor, depth);
|
||||
if (val[key] === prop.value)
|
||||
return this;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
var non_converting_unary = makePredicate("! typeof void");
|
||||
def_eval(AST_UnaryPrefix, function (compressor, depth) {
|
||||
var e = this.expression;
|
||||
// Function would be evaluated to an array and so typeof would
|
||||
// incorrectly return 'object'. Hence making is a special case.
|
||||
if (compressor.option("typeofs")
|
||||
&& this.operator == "typeof"
|
||||
&& (e instanceof AST_Lambda
|
||||
|| e instanceof AST_SymbolRef
|
||||
&& e.fixed_value() instanceof AST_Lambda)) {
|
||||
return typeof function () { };
|
||||
}
|
||||
if (!non_converting_unary.has(this.operator))
|
||||
depth++;
|
||||
e = e._eval(compressor, depth);
|
||||
if (e === this.expression)
|
||||
return this;
|
||||
switch (this.operator) {
|
||||
case "!": return !e;
|
||||
case "typeof":
|
||||
// typeof <RegExp> returns "object" or "function" on different platforms
|
||||
// so cannot evaluate reliably
|
||||
if (e instanceof RegExp)
|
||||
return this;
|
||||
return typeof e;
|
||||
case "void": return void e;
|
||||
case "~": return ~e;
|
||||
case "-": return -e;
|
||||
case "+": return +e;
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
var non_converting_binary = makePredicate("&& || ?? === !==");
|
||||
const identity_comparison = makePredicate("== != === !==");
|
||||
const has_identity = value => typeof value === "object"
|
||||
|| typeof value === "function"
|
||||
|| typeof value === "symbol";
|
||||
|
||||
def_eval(AST_Binary, function (compressor, depth) {
|
||||
if (!non_converting_binary.has(this.operator))
|
||||
depth++;
|
||||
|
||||
var left = this.left._eval(compressor, depth);
|
||||
if (left === this.left)
|
||||
return this;
|
||||
var right = this.right._eval(compressor, depth);
|
||||
if (right === this.right)
|
||||
return this;
|
||||
var result;
|
||||
|
||||
if (left != null
|
||||
&& right != null
|
||||
&& identity_comparison.has(this.operator)
|
||||
&& has_identity(left)
|
||||
&& has_identity(right)
|
||||
&& typeof left === typeof right) {
|
||||
// Do not compare by reference
|
||||
return this;
|
||||
}
|
||||
|
||||
switch (this.operator) {
|
||||
case "&&": result = left && right; break;
|
||||
case "||": result = left || right; break;
|
||||
case "??": result = left != null ? left : right; break;
|
||||
case "|": result = left | right; break;
|
||||
case "&": result = left & right; break;
|
||||
case "^": result = left ^ right; break;
|
||||
case "+": result = left + right; break;
|
||||
case "*": result = left * right; break;
|
||||
case "**": result = Math.pow(left, right); break;
|
||||
case "/": result = left / right; break;
|
||||
case "%": result = left % right; break;
|
||||
case "-": result = left - right; break;
|
||||
case "<<": result = left << right; break;
|
||||
case ">>": result = left >> right; break;
|
||||
case ">>>": result = left >>> right; break;
|
||||
case "==": result = left == right; break;
|
||||
case "===": result = left === right; break;
|
||||
case "!=": result = left != right; break;
|
||||
case "!==": result = left !== right; break;
|
||||
case "<": result = left < right; break;
|
||||
case "<=": result = left <= right; break;
|
||||
case ">": result = left > right; break;
|
||||
case ">=": result = left >= right; break;
|
||||
default:
|
||||
return this;
|
||||
}
|
||||
if (isNaN(result) && compressor.find_parent(AST_With)) {
|
||||
// leave original expression as is
|
||||
return this;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
def_eval(AST_Conditional, function (compressor, depth) {
|
||||
var condition = this.condition._eval(compressor, depth);
|
||||
if (condition === this.condition)
|
||||
return this;
|
||||
var node = condition ? this.consequent : this.alternative;
|
||||
var value = node._eval(compressor, depth);
|
||||
return value === node ? this : value;
|
||||
});
|
||||
|
||||
// Set of AST_SymbolRef which are currently being evaluated.
|
||||
// Avoids infinite recursion of ._eval()
|
||||
const reentrant_ref_eval = new Set();
|
||||
def_eval(AST_SymbolRef, function (compressor, depth) {
|
||||
if (reentrant_ref_eval.has(this))
|
||||
return this;
|
||||
|
||||
var fixed = this.fixed_value();
|
||||
if (!fixed)
|
||||
return this;
|
||||
|
||||
reentrant_ref_eval.add(this);
|
||||
const value = fixed._eval(compressor, depth);
|
||||
reentrant_ref_eval.delete(this);
|
||||
|
||||
if (value === fixed)
|
||||
return this;
|
||||
|
||||
if (value && typeof value == "object") {
|
||||
var escaped = this.definition().escaped;
|
||||
if (escaped && depth > escaped)
|
||||
return this;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
const global_objs = { Array, Math, Number, Object, String };
|
||||
|
||||
const regexp_flags = new Set([
|
||||
"dotAll",
|
||||
"global",
|
||||
"ignoreCase",
|
||||
"multiline",
|
||||
"sticky",
|
||||
"unicode",
|
||||
]);
|
||||
|
||||
def_eval(AST_PropAccess, function (compressor, depth) {
|
||||
let obj = this.expression._eval(compressor, depth + 1);
|
||||
if (obj === nullish || (this.optional && obj == null)) return nullish;
|
||||
|
||||
// `.length` of strings and arrays is always safe
|
||||
if (this.property === "length") {
|
||||
if (typeof obj === "string") {
|
||||
return obj.length;
|
||||
}
|
||||
|
||||
const is_spreadless_array =
|
||||
obj instanceof AST_Array
|
||||
&& obj.elements.every(el => !(el instanceof AST_Expansion));
|
||||
|
||||
if (
|
||||
is_spreadless_array
|
||||
&& obj.elements.every(el => !el.has_side_effects(compressor))
|
||||
) {
|
||||
return obj.elements.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (compressor.option("unsafe")) {
|
||||
var key = this.property;
|
||||
if (key instanceof AST_Node) {
|
||||
key = key._eval(compressor, depth);
|
||||
if (key === this.property)
|
||||
return this;
|
||||
}
|
||||
|
||||
var exp = this.expression;
|
||||
if (is_undeclared_ref(exp)) {
|
||||
var aa;
|
||||
var first_arg = exp.name === "hasOwnProperty"
|
||||
&& key === "call"
|
||||
&& (aa = compressor.parent() && compressor.parent().args)
|
||||
&& (aa && aa[0]
|
||||
&& aa[0].evaluate(compressor));
|
||||
|
||||
first_arg = first_arg instanceof AST_Dot ? first_arg.expression : first_arg;
|
||||
|
||||
if (first_arg == null || first_arg.thedef && first_arg.thedef.undeclared) {
|
||||
return this.clone();
|
||||
}
|
||||
if (!is_pure_native_value(exp.name, key))
|
||||
return this;
|
||||
obj = global_objs[exp.name];
|
||||
} else {
|
||||
if (obj instanceof RegExp) {
|
||||
if (key == "source") {
|
||||
return regexp_source_fix(obj.source);
|
||||
} else if (key == "flags" || regexp_flags.has(key)) {
|
||||
return obj[key];
|
||||
}
|
||||
}
|
||||
if (!obj || obj === exp || !HOP(obj, key))
|
||||
return this;
|
||||
|
||||
if (typeof obj == "function")
|
||||
switch (key) {
|
||||
case "name":
|
||||
return obj.node.name ? obj.node.name.name : "";
|
||||
case "length":
|
||||
return obj.node.length_property();
|
||||
default:
|
||||
return this;
|
||||
}
|
||||
}
|
||||
return obj[key];
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
def_eval(AST_Chain, function (compressor, depth) {
|
||||
const evaluated = this.expression._eval(compressor, depth);
|
||||
return evaluated === nullish
|
||||
? undefined
|
||||
: evaluated === this.expression
|
||||
? this
|
||||
: evaluated;
|
||||
});
|
||||
|
||||
def_eval(AST_Call, function (compressor, depth) {
|
||||
var exp = this.expression;
|
||||
|
||||
const callee = exp._eval(compressor, depth);
|
||||
if (callee === nullish || (this.optional && callee == null)) return nullish;
|
||||
|
||||
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
||||
var key = exp.property;
|
||||
if (key instanceof AST_Node) {
|
||||
key = key._eval(compressor, depth);
|
||||
if (key === exp.property)
|
||||
return this;
|
||||
}
|
||||
var val;
|
||||
var e = exp.expression;
|
||||
if (is_undeclared_ref(e)) {
|
||||
var first_arg = e.name === "hasOwnProperty" &&
|
||||
key === "call" &&
|
||||
(this.args[0] && this.args[0].evaluate(compressor));
|
||||
|
||||
first_arg = first_arg instanceof AST_Dot ? first_arg.expression : first_arg;
|
||||
|
||||
if ((first_arg == null || first_arg.thedef && first_arg.thedef.undeclared)) {
|
||||
return this.clone();
|
||||
}
|
||||
if (!is_pure_native_fn(e.name, key)) return this;
|
||||
val = global_objs[e.name];
|
||||
} else {
|
||||
val = e._eval(compressor, depth + 1);
|
||||
if (val === e || !val)
|
||||
return this;
|
||||
if (!is_pure_native_method(val.constructor.name, key))
|
||||
return this;
|
||||
}
|
||||
var args = [];
|
||||
for (var i = 0, len = this.args.length; i < len; i++) {
|
||||
var arg = this.args[i];
|
||||
var value = arg._eval(compressor, depth);
|
||||
if (arg === value)
|
||||
return this;
|
||||
if (arg instanceof AST_Lambda)
|
||||
return this;
|
||||
args.push(value);
|
||||
}
|
||||
try {
|
||||
return val[key].apply(val, args);
|
||||
} catch (ex) {
|
||||
// We don't really care
|
||||
}
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
// Also a subclass of AST_Call
|
||||
def_eval(AST_New, return_this);
|
92
my-app/node_modules/terser/lib/compress/global-defs.js
generated
vendored
Executable file
92
my-app/node_modules/terser/lib/compress/global-defs.js
generated
vendored
Executable file
|
@ -0,0 +1,92 @@
|
|||
import {
|
||||
AST_Array,
|
||||
AST_Chain,
|
||||
AST_Constant,
|
||||
AST_Dot,
|
||||
AST_ImportMeta,
|
||||
AST_Node,
|
||||
AST_Object,
|
||||
AST_ObjectKeyVal,
|
||||
AST_PropAccess,
|
||||
AST_SymbolDeclaration,
|
||||
AST_SymbolRef,
|
||||
AST_Toplevel,
|
||||
TreeTransformer,
|
||||
} from "../ast.js";
|
||||
import { make_node, noop, HOP } from "../utils/index.js";
|
||||
import { make_node_from_constant } from "./common.js";
|
||||
import { is_lhs } from "./inference.js";
|
||||
|
||||
(function(def_find_defs) {
|
||||
function to_node(value, orig) {
|
||||
if (value instanceof AST_Node) {
|
||||
if (!(value instanceof AST_Constant)) {
|
||||
// Value may be a function, an array including functions and even a complex assign / block expression,
|
||||
// so it should never be shared in different places.
|
||||
// Otherwise wrong information may be used in the compression phase
|
||||
value = value.clone(true);
|
||||
}
|
||||
return make_node(value.CTOR, orig, value);
|
||||
}
|
||||
if (Array.isArray(value)) return make_node(AST_Array, orig, {
|
||||
elements: value.map(function(value) {
|
||||
return to_node(value, orig);
|
||||
})
|
||||
});
|
||||
if (value && typeof value == "object") {
|
||||
var props = [];
|
||||
for (var key in value) if (HOP(value, key)) {
|
||||
props.push(make_node(AST_ObjectKeyVal, orig, {
|
||||
key: key,
|
||||
value: to_node(value[key], orig)
|
||||
}));
|
||||
}
|
||||
return make_node(AST_Object, orig, {
|
||||
properties: props
|
||||
});
|
||||
}
|
||||
return make_node_from_constant(value, orig);
|
||||
}
|
||||
|
||||
AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
|
||||
if (!compressor.option("global_defs")) return this;
|
||||
this.figure_out_scope({ ie8: compressor.option("ie8") });
|
||||
return this.transform(new TreeTransformer(function(node) {
|
||||
var def = node._find_defs(compressor, "");
|
||||
if (!def) return;
|
||||
var level = 0, child = node, parent;
|
||||
while (parent = this.parent(level++)) {
|
||||
if (!(parent instanceof AST_PropAccess)) break;
|
||||
if (parent.expression !== child) break;
|
||||
child = parent;
|
||||
}
|
||||
if (is_lhs(child, parent)) {
|
||||
return;
|
||||
}
|
||||
return def;
|
||||
}));
|
||||
});
|
||||
def_find_defs(AST_Node, noop);
|
||||
def_find_defs(AST_Chain, function(compressor, suffix) {
|
||||
return this.expression._find_defs(compressor, suffix);
|
||||
});
|
||||
def_find_defs(AST_Dot, function(compressor, suffix) {
|
||||
return this.expression._find_defs(compressor, "." + this.property + suffix);
|
||||
});
|
||||
def_find_defs(AST_SymbolDeclaration, function() {
|
||||
if (!this.global()) return;
|
||||
});
|
||||
def_find_defs(AST_SymbolRef, function(compressor, suffix) {
|
||||
if (!this.global()) return;
|
||||
var defines = compressor.option("global_defs");
|
||||
var name = this.name + suffix;
|
||||
if (HOP(defines, name)) return to_node(defines[name], this);
|
||||
});
|
||||
def_find_defs(AST_ImportMeta, function(compressor, suffix) {
|
||||
var defines = compressor.option("global_defs");
|
||||
var name = "import.meta" + suffix;
|
||||
if (HOP(defines, name)) return to_node(defines[name], this);
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("_find_defs", func);
|
||||
});
|
3795
my-app/node_modules/terser/lib/compress/index.js
generated
vendored
Executable file
3795
my-app/node_modules/terser/lib/compress/index.js
generated
vendored
Executable file
File diff suppressed because it is too large
Load diff
934
my-app/node_modules/terser/lib/compress/inference.js
generated
vendored
Executable file
934
my-app/node_modules/terser/lib/compress/inference.js
generated
vendored
Executable file
|
@ -0,0 +1,934 @@
|
|||
/***********************************************************************
|
||||
|
||||
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||
https://github.com/mishoo/UglifyJS2
|
||||
|
||||
-------------------------------- (C) ---------------------------------
|
||||
|
||||
Author: Mihai Bazon
|
||||
<mihai.bazon@gmail.com>
|
||||
http://mihai.bazon.net/blog
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
import {
|
||||
AST_Array,
|
||||
AST_Arrow,
|
||||
AST_Assign,
|
||||
AST_BigInt,
|
||||
AST_Binary,
|
||||
AST_Block,
|
||||
AST_BlockStatement,
|
||||
AST_Call,
|
||||
AST_Case,
|
||||
AST_Chain,
|
||||
AST_Class,
|
||||
AST_DefClass,
|
||||
AST_ClassStaticBlock,
|
||||
AST_ClassProperty,
|
||||
AST_ConciseMethod,
|
||||
AST_Conditional,
|
||||
AST_Constant,
|
||||
AST_Definitions,
|
||||
AST_Dot,
|
||||
AST_EmptyStatement,
|
||||
AST_Expansion,
|
||||
AST_False,
|
||||
AST_ForIn,
|
||||
AST_Function,
|
||||
AST_If,
|
||||
AST_Import,
|
||||
AST_ImportMeta,
|
||||
AST_Jump,
|
||||
AST_LabeledStatement,
|
||||
AST_Lambda,
|
||||
AST_New,
|
||||
AST_Node,
|
||||
AST_Null,
|
||||
AST_Number,
|
||||
AST_Object,
|
||||
AST_ObjectGetter,
|
||||
AST_ObjectKeyVal,
|
||||
AST_ObjectProperty,
|
||||
AST_ObjectSetter,
|
||||
AST_PropAccess,
|
||||
AST_RegExp,
|
||||
AST_Return,
|
||||
AST_Scope,
|
||||
AST_Sequence,
|
||||
AST_SimpleStatement,
|
||||
AST_Statement,
|
||||
AST_String,
|
||||
AST_Sub,
|
||||
AST_Switch,
|
||||
AST_SwitchBranch,
|
||||
AST_SymbolClassProperty,
|
||||
AST_SymbolDeclaration,
|
||||
AST_SymbolRef,
|
||||
AST_TemplateSegment,
|
||||
AST_TemplateString,
|
||||
AST_This,
|
||||
AST_True,
|
||||
AST_Try,
|
||||
AST_Unary,
|
||||
AST_UnaryPostfix,
|
||||
AST_UnaryPrefix,
|
||||
AST_Undefined,
|
||||
AST_VarDef,
|
||||
|
||||
walk,
|
||||
walk_abort,
|
||||
|
||||
_PURE
|
||||
} from "../ast.js";
|
||||
import {
|
||||
makePredicate,
|
||||
return_true,
|
||||
return_false,
|
||||
return_null,
|
||||
return_this,
|
||||
make_node,
|
||||
member,
|
||||
has_annotation,
|
||||
} from "../utils/index.js";
|
||||
import { make_sequence, best_of_expression, read_property } from "./common.js";
|
||||
|
||||
import { INLINED, UNDEFINED, has_flag } from "./compressor-flags.js";
|
||||
import { pure_prop_access_globals, is_pure_native_fn, is_pure_native_method } from "./native-objects.js";
|
||||
|
||||
// Functions and methods to infer certain facts about expressions
|
||||
// It's not always possible to be 100% sure about something just by static analysis,
|
||||
// so `true` means yes, and `false` means maybe
|
||||
|
||||
export const is_undeclared_ref = (node) =>
|
||||
node instanceof AST_SymbolRef && node.definition().undeclared;
|
||||
|
||||
export const lazy_op = makePredicate("&& || ??");
|
||||
export const unary_side_effects = makePredicate("delete ++ --");
|
||||
|
||||
// methods to determine whether an expression has a boolean result type
|
||||
(function(def_is_boolean) {
|
||||
const unary_bool = makePredicate("! delete");
|
||||
const binary_bool = makePredicate("in instanceof == != === !== < <= >= >");
|
||||
def_is_boolean(AST_Node, return_false);
|
||||
def_is_boolean(AST_UnaryPrefix, function() {
|
||||
return unary_bool.has(this.operator);
|
||||
});
|
||||
def_is_boolean(AST_Binary, function() {
|
||||
return binary_bool.has(this.operator)
|
||||
|| lazy_op.has(this.operator)
|
||||
&& this.left.is_boolean()
|
||||
&& this.right.is_boolean();
|
||||
});
|
||||
def_is_boolean(AST_Conditional, function() {
|
||||
return this.consequent.is_boolean() && this.alternative.is_boolean();
|
||||
});
|
||||
def_is_boolean(AST_Assign, function() {
|
||||
return this.operator == "=" && this.right.is_boolean();
|
||||
});
|
||||
def_is_boolean(AST_Sequence, function() {
|
||||
return this.tail_node().is_boolean();
|
||||
});
|
||||
def_is_boolean(AST_True, return_true);
|
||||
def_is_boolean(AST_False, return_true);
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("is_boolean", func);
|
||||
});
|
||||
|
||||
// methods to determine if an expression has a numeric result type
|
||||
(function(def_is_number) {
|
||||
def_is_number(AST_Node, return_false);
|
||||
def_is_number(AST_Number, return_true);
|
||||
const unary = makePredicate("+ - ~ ++ --");
|
||||
def_is_number(AST_Unary, function() {
|
||||
return unary.has(this.operator) && !(this.expression instanceof AST_BigInt);
|
||||
});
|
||||
const numeric_ops = makePredicate("- * / % & | ^ << >> >>>");
|
||||
def_is_number(AST_Binary, function(compressor) {
|
||||
return numeric_ops.has(this.operator) || this.operator == "+"
|
||||
&& this.left.is_number(compressor)
|
||||
&& this.right.is_number(compressor);
|
||||
});
|
||||
def_is_number(AST_Assign, function(compressor) {
|
||||
return numeric_ops.has(this.operator.slice(0, -1))
|
||||
|| this.operator == "=" && this.right.is_number(compressor);
|
||||
});
|
||||
def_is_number(AST_Sequence, function(compressor) {
|
||||
return this.tail_node().is_number(compressor);
|
||||
});
|
||||
def_is_number(AST_Conditional, function(compressor) {
|
||||
return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("is_number", func);
|
||||
});
|
||||
|
||||
// methods to determine if an expression has a string result type
|
||||
(function(def_is_string) {
|
||||
def_is_string(AST_Node, return_false);
|
||||
def_is_string(AST_String, return_true);
|
||||
def_is_string(AST_TemplateString, return_true);
|
||||
def_is_string(AST_UnaryPrefix, function() {
|
||||
return this.operator == "typeof";
|
||||
});
|
||||
def_is_string(AST_Binary, function(compressor) {
|
||||
return this.operator == "+" &&
|
||||
(this.left.is_string(compressor) || this.right.is_string(compressor));
|
||||
});
|
||||
def_is_string(AST_Assign, function(compressor) {
|
||||
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
|
||||
});
|
||||
def_is_string(AST_Sequence, function(compressor) {
|
||||
return this.tail_node().is_string(compressor);
|
||||
});
|
||||
def_is_string(AST_Conditional, function(compressor) {
|
||||
return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("is_string", func);
|
||||
});
|
||||
|
||||
export function is_undefined(node, compressor) {
|
||||
return (
|
||||
has_flag(node, UNDEFINED)
|
||||
|| node instanceof AST_Undefined
|
||||
|| node instanceof AST_UnaryPrefix
|
||||
&& node.operator == "void"
|
||||
&& !node.expression.has_side_effects(compressor)
|
||||
);
|
||||
}
|
||||
|
||||
// Is the node explicitly null or undefined.
|
||||
function is_null_or_undefined(node, compressor) {
|
||||
let fixed;
|
||||
return (
|
||||
node instanceof AST_Null
|
||||
|| is_undefined(node, compressor)
|
||||
|| (
|
||||
node instanceof AST_SymbolRef
|
||||
&& (fixed = node.definition().fixed) instanceof AST_Node
|
||||
&& is_nullish(fixed, compressor)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Find out if this expression is optionally chained from a base-point that we
|
||||
// can statically analyze as null or undefined.
|
||||
export function is_nullish_shortcircuited(node, compressor) {
|
||||
if (node instanceof AST_PropAccess || node instanceof AST_Call) {
|
||||
return (
|
||||
(node.optional && is_null_or_undefined(node.expression, compressor))
|
||||
|| is_nullish_shortcircuited(node.expression, compressor)
|
||||
);
|
||||
}
|
||||
if (node instanceof AST_Chain) return is_nullish_shortcircuited(node.expression, compressor);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find out if something is == null, or can short circuit into nullish.
|
||||
// Used to optimize ?. and ??
|
||||
export function is_nullish(node, compressor) {
|
||||
if (is_null_or_undefined(node, compressor)) return true;
|
||||
return is_nullish_shortcircuited(node, compressor);
|
||||
}
|
||||
|
||||
// Determine if expression might cause side effects
|
||||
// If there's a possibility that a node may change something when it's executed, this returns true
|
||||
(function(def_has_side_effects) {
|
||||
def_has_side_effects(AST_Node, return_true);
|
||||
|
||||
def_has_side_effects(AST_EmptyStatement, return_false);
|
||||
def_has_side_effects(AST_Constant, return_false);
|
||||
def_has_side_effects(AST_This, return_false);
|
||||
|
||||
function any(list, compressor) {
|
||||
for (var i = list.length; --i >= 0;)
|
||||
if (list[i].has_side_effects(compressor))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
def_has_side_effects(AST_Block, function(compressor) {
|
||||
return any(this.body, compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Call, function(compressor) {
|
||||
if (
|
||||
!this.is_callee_pure(compressor)
|
||||
&& (!this.expression.is_call_pure(compressor)
|
||||
|| this.expression.has_side_effects(compressor))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return any(this.args, compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Switch, function(compressor) {
|
||||
return this.expression.has_side_effects(compressor)
|
||||
|| any(this.body, compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Case, function(compressor) {
|
||||
return this.expression.has_side_effects(compressor)
|
||||
|| any(this.body, compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Try, function(compressor) {
|
||||
return this.body.has_side_effects(compressor)
|
||||
|| this.bcatch && this.bcatch.has_side_effects(compressor)
|
||||
|| this.bfinally && this.bfinally.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_If, function(compressor) {
|
||||
return this.condition.has_side_effects(compressor)
|
||||
|| this.body && this.body.has_side_effects(compressor)
|
||||
|| this.alternative && this.alternative.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_ImportMeta, return_false);
|
||||
def_has_side_effects(AST_LabeledStatement, function(compressor) {
|
||||
return this.body.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_SimpleStatement, function(compressor) {
|
||||
return this.body.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Lambda, return_false);
|
||||
def_has_side_effects(AST_Class, function (compressor) {
|
||||
if (this.extends && this.extends.has_side_effects(compressor)) {
|
||||
return true;
|
||||
}
|
||||
return any(this.properties, compressor);
|
||||
});
|
||||
def_has_side_effects(AST_ClassStaticBlock, function(compressor) {
|
||||
return any(this.body, compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Binary, function(compressor) {
|
||||
return this.left.has_side_effects(compressor)
|
||||
|| this.right.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Assign, return_true);
|
||||
def_has_side_effects(AST_Conditional, function(compressor) {
|
||||
return this.condition.has_side_effects(compressor)
|
||||
|| this.consequent.has_side_effects(compressor)
|
||||
|| this.alternative.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Unary, function(compressor) {
|
||||
return unary_side_effects.has(this.operator)
|
||||
|| this.expression.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_SymbolRef, function(compressor) {
|
||||
return !this.is_declared(compressor) && !pure_prop_access_globals.has(this.name);
|
||||
});
|
||||
def_has_side_effects(AST_SymbolClassProperty, return_false);
|
||||
def_has_side_effects(AST_SymbolDeclaration, return_false);
|
||||
def_has_side_effects(AST_Object, function(compressor) {
|
||||
return any(this.properties, compressor);
|
||||
});
|
||||
def_has_side_effects(AST_ObjectProperty, function(compressor) {
|
||||
return (
|
||||
this.computed_key() && this.key.has_side_effects(compressor)
|
||||
|| this.value && this.value.has_side_effects(compressor)
|
||||
);
|
||||
});
|
||||
def_has_side_effects(AST_ClassProperty, function(compressor) {
|
||||
return (
|
||||
this.computed_key() && this.key.has_side_effects(compressor)
|
||||
|| this.static && this.value && this.value.has_side_effects(compressor)
|
||||
);
|
||||
});
|
||||
def_has_side_effects(AST_ConciseMethod, function(compressor) {
|
||||
return this.computed_key() && this.key.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_ObjectGetter, function(compressor) {
|
||||
return this.computed_key() && this.key.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_ObjectSetter, function(compressor) {
|
||||
return this.computed_key() && this.key.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Array, function(compressor) {
|
||||
return any(this.elements, compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Dot, function(compressor) {
|
||||
if (is_nullish(this, compressor)) {
|
||||
return this.expression.has_side_effects(compressor);
|
||||
}
|
||||
if (!this.optional && this.expression.may_throw_on_access(compressor)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.expression.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Sub, function(compressor) {
|
||||
if (is_nullish(this, compressor)) {
|
||||
return this.expression.has_side_effects(compressor);
|
||||
}
|
||||
if (!this.optional && this.expression.may_throw_on_access(compressor)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var property = this.property.has_side_effects(compressor);
|
||||
if (property && this.optional) return true; // "?." is a condition
|
||||
|
||||
return property || this.expression.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Chain, function (compressor) {
|
||||
return this.expression.has_side_effects(compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Sequence, function(compressor) {
|
||||
return any(this.expressions, compressor);
|
||||
});
|
||||
def_has_side_effects(AST_Definitions, function(compressor) {
|
||||
return any(this.definitions, compressor);
|
||||
});
|
||||
def_has_side_effects(AST_VarDef, function() {
|
||||
return this.value;
|
||||
});
|
||||
def_has_side_effects(AST_TemplateSegment, return_false);
|
||||
def_has_side_effects(AST_TemplateString, function(compressor) {
|
||||
return any(this.segments, compressor);
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("has_side_effects", func);
|
||||
});
|
||||
|
||||
// determine if expression may throw
|
||||
(function(def_may_throw) {
|
||||
def_may_throw(AST_Node, return_true);
|
||||
|
||||
def_may_throw(AST_Constant, return_false);
|
||||
def_may_throw(AST_EmptyStatement, return_false);
|
||||
def_may_throw(AST_Lambda, return_false);
|
||||
def_may_throw(AST_SymbolDeclaration, return_false);
|
||||
def_may_throw(AST_This, return_false);
|
||||
def_may_throw(AST_ImportMeta, return_false);
|
||||
|
||||
function any(list, compressor) {
|
||||
for (var i = list.length; --i >= 0;)
|
||||
if (list[i].may_throw(compressor))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
def_may_throw(AST_Class, function(compressor) {
|
||||
if (this.extends && this.extends.may_throw(compressor)) return true;
|
||||
return any(this.properties, compressor);
|
||||
});
|
||||
def_may_throw(AST_ClassStaticBlock, function (compressor) {
|
||||
return any(this.body, compressor);
|
||||
});
|
||||
|
||||
def_may_throw(AST_Array, function(compressor) {
|
||||
return any(this.elements, compressor);
|
||||
});
|
||||
def_may_throw(AST_Assign, function(compressor) {
|
||||
if (this.right.may_throw(compressor)) return true;
|
||||
if (!compressor.has_directive("use strict")
|
||||
&& this.operator == "="
|
||||
&& this.left instanceof AST_SymbolRef) {
|
||||
return false;
|
||||
}
|
||||
return this.left.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Binary, function(compressor) {
|
||||
return this.left.may_throw(compressor)
|
||||
|| this.right.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Block, function(compressor) {
|
||||
return any(this.body, compressor);
|
||||
});
|
||||
def_may_throw(AST_Call, function(compressor) {
|
||||
if (is_nullish(this, compressor)) return false;
|
||||
if (any(this.args, compressor)) return true;
|
||||
if (this.is_callee_pure(compressor)) return false;
|
||||
if (this.expression.may_throw(compressor)) return true;
|
||||
return !(this.expression instanceof AST_Lambda)
|
||||
|| any(this.expression.body, compressor);
|
||||
});
|
||||
def_may_throw(AST_Case, function(compressor) {
|
||||
return this.expression.may_throw(compressor)
|
||||
|| any(this.body, compressor);
|
||||
});
|
||||
def_may_throw(AST_Conditional, function(compressor) {
|
||||
return this.condition.may_throw(compressor)
|
||||
|| this.consequent.may_throw(compressor)
|
||||
|| this.alternative.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Definitions, function(compressor) {
|
||||
return any(this.definitions, compressor);
|
||||
});
|
||||
def_may_throw(AST_If, function(compressor) {
|
||||
return this.condition.may_throw(compressor)
|
||||
|| this.body && this.body.may_throw(compressor)
|
||||
|| this.alternative && this.alternative.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_LabeledStatement, function(compressor) {
|
||||
return this.body.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Object, function(compressor) {
|
||||
return any(this.properties, compressor);
|
||||
});
|
||||
def_may_throw(AST_ObjectProperty, function(compressor) {
|
||||
// TODO key may throw too
|
||||
return this.value ? this.value.may_throw(compressor) : false;
|
||||
});
|
||||
def_may_throw(AST_ClassProperty, function(compressor) {
|
||||
return (
|
||||
this.computed_key() && this.key.may_throw(compressor)
|
||||
|| this.static && this.value && this.value.may_throw(compressor)
|
||||
);
|
||||
});
|
||||
def_may_throw(AST_ConciseMethod, function(compressor) {
|
||||
return this.computed_key() && this.key.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_ObjectGetter, function(compressor) {
|
||||
return this.computed_key() && this.key.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_ObjectSetter, function(compressor) {
|
||||
return this.computed_key() && this.key.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Return, function(compressor) {
|
||||
return this.value && this.value.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Sequence, function(compressor) {
|
||||
return any(this.expressions, compressor);
|
||||
});
|
||||
def_may_throw(AST_SimpleStatement, function(compressor) {
|
||||
return this.body.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Dot, function(compressor) {
|
||||
if (is_nullish(this, compressor)) return false;
|
||||
return !this.optional && this.expression.may_throw_on_access(compressor)
|
||||
|| this.expression.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Sub, function(compressor) {
|
||||
if (is_nullish(this, compressor)) return false;
|
||||
return !this.optional && this.expression.may_throw_on_access(compressor)
|
||||
|| this.expression.may_throw(compressor)
|
||||
|| this.property.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Chain, function(compressor) {
|
||||
return this.expression.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Switch, function(compressor) {
|
||||
return this.expression.may_throw(compressor)
|
||||
|| any(this.body, compressor);
|
||||
});
|
||||
def_may_throw(AST_SymbolRef, function(compressor) {
|
||||
return !this.is_declared(compressor) && !pure_prop_access_globals.has(this.name);
|
||||
});
|
||||
def_may_throw(AST_SymbolClassProperty, return_false);
|
||||
def_may_throw(AST_Try, function(compressor) {
|
||||
return this.bcatch ? this.bcatch.may_throw(compressor) : this.body.may_throw(compressor)
|
||||
|| this.bfinally && this.bfinally.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_Unary, function(compressor) {
|
||||
if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef)
|
||||
return false;
|
||||
return this.expression.may_throw(compressor);
|
||||
});
|
||||
def_may_throw(AST_VarDef, function(compressor) {
|
||||
if (!this.value) return false;
|
||||
return this.value.may_throw(compressor);
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("may_throw", func);
|
||||
});
|
||||
|
||||
// determine if expression is constant
|
||||
(function(def_is_constant_expression) {
|
||||
function all_refs_local(scope) {
|
||||
let result = true;
|
||||
walk(this, node => {
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
if (has_flag(this, INLINED)) {
|
||||
result = false;
|
||||
return walk_abort;
|
||||
}
|
||||
var def = node.definition();
|
||||
if (
|
||||
member(def, this.enclosed)
|
||||
&& !this.variables.has(def.name)
|
||||
) {
|
||||
if (scope) {
|
||||
var scope_def = scope.find_variable(node);
|
||||
if (def.undeclared ? !scope_def : scope_def === def) {
|
||||
result = "f";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
result = false;
|
||||
return walk_abort;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_This && this instanceof AST_Arrow) {
|
||||
result = false;
|
||||
return walk_abort;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
def_is_constant_expression(AST_Node, return_false);
|
||||
def_is_constant_expression(AST_Constant, return_true);
|
||||
def_is_constant_expression(AST_Class, function(scope) {
|
||||
if (this.extends && !this.extends.is_constant_expression(scope)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const prop of this.properties) {
|
||||
if (prop.computed_key() && !prop.key.is_constant_expression(scope)) {
|
||||
return false;
|
||||
}
|
||||
if (prop.static && prop.value && !prop.value.is_constant_expression(scope)) {
|
||||
return false;
|
||||
}
|
||||
if (prop instanceof AST_ClassStaticBlock) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return all_refs_local.call(this, scope);
|
||||
});
|
||||
def_is_constant_expression(AST_Lambda, all_refs_local);
|
||||
def_is_constant_expression(AST_Unary, function() {
|
||||
return this.expression.is_constant_expression();
|
||||
});
|
||||
def_is_constant_expression(AST_Binary, function() {
|
||||
return this.left.is_constant_expression()
|
||||
&& this.right.is_constant_expression();
|
||||
});
|
||||
def_is_constant_expression(AST_Array, function() {
|
||||
return this.elements.every((l) => l.is_constant_expression());
|
||||
});
|
||||
def_is_constant_expression(AST_Object, function() {
|
||||
return this.properties.every((l) => l.is_constant_expression());
|
||||
});
|
||||
def_is_constant_expression(AST_ObjectProperty, function() {
|
||||
return !!(!(this.key instanceof AST_Node) && this.value && this.value.is_constant_expression());
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("is_constant_expression", func);
|
||||
});
|
||||
|
||||
|
||||
// may_throw_on_access()
|
||||
// returns true if this node may be null, undefined or contain `AST_Accessor`
|
||||
(function(def_may_throw_on_access) {
|
||||
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
|
||||
return !compressor.option("pure_getters")
|
||||
|| this._dot_throw(compressor);
|
||||
});
|
||||
|
||||
function is_strict(compressor) {
|
||||
return /strict/.test(compressor.option("pure_getters"));
|
||||
}
|
||||
|
||||
def_may_throw_on_access(AST_Node, is_strict);
|
||||
def_may_throw_on_access(AST_Null, return_true);
|
||||
def_may_throw_on_access(AST_Undefined, return_true);
|
||||
def_may_throw_on_access(AST_Constant, return_false);
|
||||
def_may_throw_on_access(AST_Array, return_false);
|
||||
def_may_throw_on_access(AST_Object, function(compressor) {
|
||||
if (!is_strict(compressor)) return false;
|
||||
for (var i = this.properties.length; --i >=0;)
|
||||
if (this.properties[i]._dot_throw(compressor)) return true;
|
||||
return false;
|
||||
});
|
||||
// Do not be as strict with classes as we are with objects.
|
||||
// Hopefully the community is not going to abuse static getters and setters.
|
||||
// https://github.com/terser/terser/issues/724#issuecomment-643655656
|
||||
def_may_throw_on_access(AST_Class, return_false);
|
||||
def_may_throw_on_access(AST_ObjectProperty, return_false);
|
||||
def_may_throw_on_access(AST_ObjectGetter, return_true);
|
||||
def_may_throw_on_access(AST_Expansion, function(compressor) {
|
||||
return this.expression._dot_throw(compressor);
|
||||
});
|
||||
def_may_throw_on_access(AST_Function, return_false);
|
||||
def_may_throw_on_access(AST_Arrow, return_false);
|
||||
def_may_throw_on_access(AST_UnaryPostfix, return_false);
|
||||
def_may_throw_on_access(AST_UnaryPrefix, function() {
|
||||
return this.operator == "void";
|
||||
});
|
||||
def_may_throw_on_access(AST_Binary, function(compressor) {
|
||||
return (this.operator == "&&" || this.operator == "||" || this.operator == "??")
|
||||
&& (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
|
||||
});
|
||||
def_may_throw_on_access(AST_Assign, function(compressor) {
|
||||
if (this.logical) return true;
|
||||
|
||||
return this.operator == "="
|
||||
&& this.right._dot_throw(compressor);
|
||||
});
|
||||
def_may_throw_on_access(AST_Conditional, function(compressor) {
|
||||
return this.consequent._dot_throw(compressor)
|
||||
|| this.alternative._dot_throw(compressor);
|
||||
});
|
||||
def_may_throw_on_access(AST_Dot, function(compressor) {
|
||||
if (!is_strict(compressor)) return false;
|
||||
|
||||
if (this.property == "prototype") {
|
||||
return !(
|
||||
this.expression instanceof AST_Function
|
||||
|| this.expression instanceof AST_Class
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
def_may_throw_on_access(AST_Chain, function(compressor) {
|
||||
return this.expression._dot_throw(compressor);
|
||||
});
|
||||
def_may_throw_on_access(AST_Sequence, function(compressor) {
|
||||
return this.tail_node()._dot_throw(compressor);
|
||||
});
|
||||
def_may_throw_on_access(AST_SymbolRef, function(compressor) {
|
||||
if (this.name === "arguments" && this.scope instanceof AST_Lambda) return false;
|
||||
if (has_flag(this, UNDEFINED)) return true;
|
||||
if (!is_strict(compressor)) return false;
|
||||
if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
|
||||
if (this.is_immutable()) return false;
|
||||
var fixed = this.fixed_value();
|
||||
return !fixed || fixed._dot_throw(compressor);
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("_dot_throw", func);
|
||||
});
|
||||
|
||||
export function is_lhs(node, parent) {
|
||||
if (parent instanceof AST_Unary && unary_side_effects.has(parent.operator)) return parent.expression;
|
||||
if (parent instanceof AST_Assign && parent.left === node) return node;
|
||||
if (parent instanceof AST_ForIn && parent.init === node) return node;
|
||||
}
|
||||
|
||||
// method to negate an expression
|
||||
(function(def_negate) {
|
||||
function basic_negation(exp) {
|
||||
return make_node(AST_UnaryPrefix, exp, {
|
||||
operator: "!",
|
||||
expression: exp
|
||||
});
|
||||
}
|
||||
function best(orig, alt, first_in_statement) {
|
||||
var negated = basic_negation(orig);
|
||||
if (first_in_statement) {
|
||||
var stat = make_node(AST_SimpleStatement, alt, {
|
||||
body: alt
|
||||
});
|
||||
return best_of_expression(negated, stat) === stat ? alt : negated;
|
||||
}
|
||||
return best_of_expression(negated, alt);
|
||||
}
|
||||
def_negate(AST_Node, function() {
|
||||
return basic_negation(this);
|
||||
});
|
||||
def_negate(AST_Statement, function() {
|
||||
throw new Error("Cannot negate a statement");
|
||||
});
|
||||
def_negate(AST_Function, function() {
|
||||
return basic_negation(this);
|
||||
});
|
||||
def_negate(AST_Class, function() {
|
||||
return basic_negation(this);
|
||||
});
|
||||
def_negate(AST_Arrow, function() {
|
||||
return basic_negation(this);
|
||||
});
|
||||
def_negate(AST_UnaryPrefix, function() {
|
||||
if (this.operator == "!")
|
||||
return this.expression;
|
||||
return basic_negation(this);
|
||||
});
|
||||
def_negate(AST_Sequence, function(compressor) {
|
||||
var expressions = this.expressions.slice();
|
||||
expressions.push(expressions.pop().negate(compressor));
|
||||
return make_sequence(this, expressions);
|
||||
});
|
||||
def_negate(AST_Conditional, function(compressor, first_in_statement) {
|
||||
var self = this.clone();
|
||||
self.consequent = self.consequent.negate(compressor);
|
||||
self.alternative = self.alternative.negate(compressor);
|
||||
return best(this, self, first_in_statement);
|
||||
});
|
||||
def_negate(AST_Binary, function(compressor, first_in_statement) {
|
||||
var self = this.clone(), op = this.operator;
|
||||
if (compressor.option("unsafe_comps")) {
|
||||
switch (op) {
|
||||
case "<=" : self.operator = ">" ; return self;
|
||||
case "<" : self.operator = ">=" ; return self;
|
||||
case ">=" : self.operator = "<" ; return self;
|
||||
case ">" : self.operator = "<=" ; return self;
|
||||
}
|
||||
}
|
||||
switch (op) {
|
||||
case "==" : self.operator = "!="; return self;
|
||||
case "!=" : self.operator = "=="; return self;
|
||||
case "===": self.operator = "!=="; return self;
|
||||
case "!==": self.operator = "==="; return self;
|
||||
case "&&":
|
||||
self.operator = "||";
|
||||
self.left = self.left.negate(compressor, first_in_statement);
|
||||
self.right = self.right.negate(compressor);
|
||||
return best(this, self, first_in_statement);
|
||||
case "||":
|
||||
self.operator = "&&";
|
||||
self.left = self.left.negate(compressor, first_in_statement);
|
||||
self.right = self.right.negate(compressor);
|
||||
return best(this, self, first_in_statement);
|
||||
}
|
||||
return basic_negation(this);
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("negate", function(compressor, first_in_statement) {
|
||||
return func.call(this, compressor, first_in_statement);
|
||||
});
|
||||
});
|
||||
|
||||
// Is the callee of this function pure?
|
||||
var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
|
||||
AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
|
||||
if (compressor.option("unsafe")) {
|
||||
var expr = this.expression;
|
||||
var first_arg = (this.args && this.args[0] && this.args[0].evaluate(compressor));
|
||||
if (
|
||||
expr.expression && expr.expression.name === "hasOwnProperty" &&
|
||||
(first_arg == null || first_arg.thedef && first_arg.thedef.undeclared)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (is_undeclared_ref(expr) && global_pure_fns.has(expr.name)) return true;
|
||||
if (
|
||||
expr instanceof AST_Dot
|
||||
&& is_undeclared_ref(expr.expression)
|
||||
&& is_pure_native_fn(expr.expression.name, expr.property)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ((this instanceof AST_New) && compressor.option("pure_new")) {
|
||||
return true;
|
||||
}
|
||||
if (compressor.option("side_effects") && has_annotation(this, _PURE)) {
|
||||
return true;
|
||||
}
|
||||
return !compressor.pure_funcs(this);
|
||||
});
|
||||
|
||||
// If I call this, is it a pure function?
|
||||
AST_Node.DEFMETHOD("is_call_pure", return_false);
|
||||
AST_Dot.DEFMETHOD("is_call_pure", function(compressor) {
|
||||
if (!compressor.option("unsafe")) return;
|
||||
const expr = this.expression;
|
||||
|
||||
let native_obj;
|
||||
if (expr instanceof AST_Array) {
|
||||
native_obj = "Array";
|
||||
} else if (expr.is_boolean()) {
|
||||
native_obj = "Boolean";
|
||||
} else if (expr.is_number(compressor)) {
|
||||
native_obj = "Number";
|
||||
} else if (expr instanceof AST_RegExp) {
|
||||
native_obj = "RegExp";
|
||||
} else if (expr.is_string(compressor)) {
|
||||
native_obj = "String";
|
||||
} else if (!this.may_throw_on_access(compressor)) {
|
||||
native_obj = "Object";
|
||||
}
|
||||
return native_obj != null && is_pure_native_method(native_obj, this.property);
|
||||
});
|
||||
|
||||
// tell me if a statement aborts
|
||||
export const aborts = (thing) => thing && thing.aborts();
|
||||
|
||||
(function(def_aborts) {
|
||||
def_aborts(AST_Statement, return_null);
|
||||
def_aborts(AST_Jump, return_this);
|
||||
function block_aborts() {
|
||||
for (var i = 0; i < this.body.length; i++) {
|
||||
if (aborts(this.body[i])) {
|
||||
return this.body[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
def_aborts(AST_Import, return_null);
|
||||
def_aborts(AST_BlockStatement, block_aborts);
|
||||
def_aborts(AST_SwitchBranch, block_aborts);
|
||||
def_aborts(AST_DefClass, function () {
|
||||
for (const prop of this.properties) {
|
||||
if (prop instanceof AST_ClassStaticBlock) {
|
||||
if (prop.aborts()) return prop;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
def_aborts(AST_ClassStaticBlock, block_aborts);
|
||||
def_aborts(AST_If, function() {
|
||||
return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("aborts", func);
|
||||
});
|
||||
|
||||
AST_Node.DEFMETHOD("contains_this", function() {
|
||||
return walk(this, node => {
|
||||
if (node instanceof AST_This) return walk_abort;
|
||||
if (
|
||||
node !== this
|
||||
&& node instanceof AST_Scope
|
||||
&& !(node instanceof AST_Arrow)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export function is_modified(compressor, tw, node, value, level, immutable) {
|
||||
var parent = tw.parent(level);
|
||||
var lhs = is_lhs(node, parent);
|
||||
if (lhs) return lhs;
|
||||
if (!immutable
|
||||
&& parent instanceof AST_Call
|
||||
&& parent.expression === node
|
||||
&& !(value instanceof AST_Arrow)
|
||||
&& !(value instanceof AST_Class)
|
||||
&& !parent.is_callee_pure(compressor)
|
||||
&& (!(value instanceof AST_Function)
|
||||
|| !(parent instanceof AST_New) && value.contains_this())) {
|
||||
return true;
|
||||
}
|
||||
if (parent instanceof AST_Array) {
|
||||
return is_modified(compressor, tw, parent, parent, level + 1);
|
||||
}
|
||||
if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
|
||||
var obj = tw.parent(level + 1);
|
||||
return is_modified(compressor, tw, obj, obj, level + 2);
|
||||
}
|
||||
if (parent instanceof AST_PropAccess && parent.expression === node) {
|
||||
var prop = read_property(value, parent.property);
|
||||
return !immutable && is_modified(compressor, tw, parent, prop, level + 1);
|
||||
}
|
||||
}
|
665
my-app/node_modules/terser/lib/compress/inline.js
generated
vendored
Executable file
665
my-app/node_modules/terser/lib/compress/inline.js
generated
vendored
Executable file
|
@ -0,0 +1,665 @@
|
|||
/***********************************************************************
|
||||
|
||||
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||
https://github.com/mishoo/UglifyJS2
|
||||
|
||||
-------------------------------- (C) ---------------------------------
|
||||
|
||||
Author: Mihai Bazon
|
||||
<mihai.bazon@gmail.com>
|
||||
http://mihai.bazon.net/blog
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
import {
|
||||
AST_Array,
|
||||
AST_Assign,
|
||||
AST_Block,
|
||||
AST_Call,
|
||||
AST_Catch,
|
||||
AST_Class,
|
||||
AST_ClassExpression,
|
||||
AST_DefaultAssign,
|
||||
AST_DefClass,
|
||||
AST_Defun,
|
||||
AST_Destructuring,
|
||||
AST_EmptyStatement,
|
||||
AST_Expansion,
|
||||
AST_Export,
|
||||
AST_Function,
|
||||
AST_IterationStatement,
|
||||
AST_Lambda,
|
||||
AST_Node,
|
||||
AST_Number,
|
||||
AST_Object,
|
||||
AST_ObjectKeyVal,
|
||||
AST_PropAccess,
|
||||
AST_Return,
|
||||
AST_Scope,
|
||||
AST_SimpleStatement,
|
||||
AST_Statement,
|
||||
AST_SymbolDefun,
|
||||
AST_SymbolFunarg,
|
||||
AST_SymbolLambda,
|
||||
AST_SymbolRef,
|
||||
AST_SymbolVar,
|
||||
AST_This,
|
||||
AST_Toplevel,
|
||||
AST_UnaryPrefix,
|
||||
AST_Undefined,
|
||||
AST_Var,
|
||||
AST_VarDef,
|
||||
|
||||
walk,
|
||||
|
||||
_INLINE,
|
||||
_NOINLINE,
|
||||
_PURE,
|
||||
} from "../ast.js";
|
||||
import { make_node, has_annotation } from "../utils/index.js";
|
||||
import "../size.js";
|
||||
|
||||
import "./evaluate.js";
|
||||
import "./drop-side-effect-free.js";
|
||||
import "./reduce-vars.js";
|
||||
import {
|
||||
SQUEEZED,
|
||||
INLINED,
|
||||
UNUSED,
|
||||
|
||||
has_flag,
|
||||
set_flag,
|
||||
} from "./compressor-flags.js";
|
||||
import {
|
||||
make_sequence,
|
||||
best_of,
|
||||
make_node_from_constant,
|
||||
identifier_atom,
|
||||
is_empty,
|
||||
is_func_expr,
|
||||
is_iife_call,
|
||||
is_reachable,
|
||||
is_recursive_ref,
|
||||
retain_top_func,
|
||||
} from "./common.js";
|
||||
|
||||
/**
|
||||
* Module that contains the inlining logic.
|
||||
*
|
||||
* @module
|
||||
*
|
||||
* The stars of the show are `inline_into_symbolref` and `inline_into_call`.
|
||||
*/
|
||||
|
||||
function within_array_or_object_literal(compressor) {
|
||||
var node, level = 0;
|
||||
while (node = compressor.parent(level++)) {
|
||||
if (node instanceof AST_Statement) return false;
|
||||
if (node instanceof AST_Array
|
||||
|| node instanceof AST_ObjectKeyVal
|
||||
|| node instanceof AST_Object) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function scope_encloses_variables_in_this_scope(scope, pulled_scope) {
|
||||
for (const enclosed of pulled_scope.enclosed) {
|
||||
if (pulled_scope.variables.has(enclosed.name)) {
|
||||
continue;
|
||||
}
|
||||
const looked_up = scope.find_variable(enclosed.name);
|
||||
if (looked_up) {
|
||||
if (looked_up === enclosed) continue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* An extra check function for `top_retain` option, compare the length of const identifier
|
||||
* and init value length and return true if init value is longer than identifier. for example:
|
||||
* ```
|
||||
* // top_retain: ["example"]
|
||||
* const example = 100
|
||||
* ```
|
||||
* it will return false because length of "100" is short than identifier "example".
|
||||
*/
|
||||
function is_const_symbol_short_than_init_value(def, fixed_value) {
|
||||
if (def.orig.length === 1 && fixed_value) {
|
||||
const init_value_length = fixed_value.size();
|
||||
const identifer_length = def.name.length;
|
||||
return init_value_length > identifer_length;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function inline_into_symbolref(self, compressor) {
|
||||
const parent = compressor.parent();
|
||||
const def = self.definition();
|
||||
const nearest_scope = compressor.find_scope();
|
||||
let fixed = self.fixed_value();
|
||||
if (
|
||||
compressor.top_retain &&
|
||||
def.global &&
|
||||
compressor.top_retain(def) &&
|
||||
// when identifier is in top_retain option dose not mean we can always inline it.
|
||||
// if identifier name is longer then init value, we can replace it.
|
||||
is_const_symbol_short_than_init_value(def, fixed)
|
||||
) {
|
||||
// keep it
|
||||
def.fixed = false;
|
||||
def.single_use = false;
|
||||
return self;
|
||||
}
|
||||
|
||||
let single_use = def.single_use
|
||||
&& !(parent instanceof AST_Call
|
||||
&& (parent.is_callee_pure(compressor))
|
||||
|| has_annotation(parent, _NOINLINE))
|
||||
&& !(parent instanceof AST_Export
|
||||
&& fixed instanceof AST_Lambda
|
||||
&& fixed.name);
|
||||
|
||||
if (single_use && fixed instanceof AST_Node) {
|
||||
single_use =
|
||||
!fixed.has_side_effects(compressor)
|
||||
&& !fixed.may_throw(compressor);
|
||||
}
|
||||
|
||||
if (fixed instanceof AST_Class && def.scope !== self.scope) {
|
||||
return self;
|
||||
}
|
||||
|
||||
if (single_use && (fixed instanceof AST_Lambda || fixed instanceof AST_Class)) {
|
||||
if (retain_top_func(fixed, compressor)) {
|
||||
single_use = false;
|
||||
} else if (def.scope !== self.scope
|
||||
&& (def.escaped == 1
|
||||
|| has_flag(fixed, INLINED)
|
||||
|| within_array_or_object_literal(compressor)
|
||||
|| !compressor.option("reduce_funcs"))) {
|
||||
single_use = false;
|
||||
} else if (is_recursive_ref(compressor, def)) {
|
||||
single_use = false;
|
||||
} else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) {
|
||||
single_use = fixed.is_constant_expression(self.scope);
|
||||
if (single_use == "f") {
|
||||
var scope = self.scope;
|
||||
do {
|
||||
if (scope instanceof AST_Defun || is_func_expr(scope)) {
|
||||
set_flag(scope, INLINED);
|
||||
}
|
||||
} while (scope = scope.parent_scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (single_use && (fixed instanceof AST_Lambda || fixed instanceof AST_Class)) {
|
||||
single_use =
|
||||
def.scope === self.scope
|
||||
&& !scope_encloses_variables_in_this_scope(nearest_scope, fixed)
|
||||
|| parent instanceof AST_Call
|
||||
&& parent.expression === self
|
||||
&& !scope_encloses_variables_in_this_scope(nearest_scope, fixed)
|
||||
&& !(fixed.name && fixed.name.definition().recursive_refs > 0);
|
||||
}
|
||||
|
||||
if (single_use && fixed) {
|
||||
if (fixed instanceof AST_DefClass) {
|
||||
set_flag(fixed, SQUEEZED);
|
||||
fixed = make_node(AST_ClassExpression, fixed, fixed);
|
||||
}
|
||||
if (fixed instanceof AST_Defun) {
|
||||
set_flag(fixed, SQUEEZED);
|
||||
fixed = make_node(AST_Function, fixed, fixed);
|
||||
}
|
||||
if (def.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) {
|
||||
const defun_def = fixed.name.definition();
|
||||
let lambda_def = fixed.variables.get(fixed.name.name);
|
||||
let name = lambda_def && lambda_def.orig[0];
|
||||
if (!(name instanceof AST_SymbolLambda)) {
|
||||
name = make_node(AST_SymbolLambda, fixed.name, fixed.name);
|
||||
name.scope = fixed;
|
||||
fixed.name = name;
|
||||
lambda_def = fixed.def_function(name);
|
||||
}
|
||||
walk(fixed, node => {
|
||||
if (node instanceof AST_SymbolRef && node.definition() === defun_def) {
|
||||
node.thedef = lambda_def;
|
||||
lambda_def.references.push(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (
|
||||
(fixed instanceof AST_Lambda || fixed instanceof AST_Class)
|
||||
&& fixed.parent_scope !== nearest_scope
|
||||
) {
|
||||
fixed = fixed.clone(true, compressor.get_toplevel());
|
||||
|
||||
nearest_scope.add_child_scope(fixed);
|
||||
}
|
||||
return fixed.optimize(compressor);
|
||||
}
|
||||
|
||||
// multiple uses
|
||||
if (fixed) {
|
||||
let replace;
|
||||
|
||||
if (fixed instanceof AST_This) {
|
||||
if (!(def.orig[0] instanceof AST_SymbolFunarg)
|
||||
&& def.references.every((ref) =>
|
||||
def.scope === ref.scope
|
||||
)) {
|
||||
replace = fixed;
|
||||
}
|
||||
} else {
|
||||
var ev = fixed.evaluate(compressor);
|
||||
if (
|
||||
ev !== fixed
|
||||
&& (compressor.option("unsafe_regexp") || !(ev instanceof RegExp))
|
||||
) {
|
||||
replace = make_node_from_constant(ev, fixed);
|
||||
}
|
||||
}
|
||||
|
||||
if (replace) {
|
||||
const name_length = self.size(compressor);
|
||||
const replace_size = replace.size(compressor);
|
||||
|
||||
let overhead = 0;
|
||||
if (compressor.option("unused") && !compressor.exposed(def)) {
|
||||
overhead =
|
||||
(name_length + 2 + replace_size) /
|
||||
(def.references.length - def.assignments);
|
||||
}
|
||||
|
||||
if (replace_size <= name_length + overhead) {
|
||||
return replace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
export function inline_into_call(self, compressor) {
|
||||
var exp = self.expression;
|
||||
var fn = exp;
|
||||
var simple_args = self.args.every((arg) => !(arg instanceof AST_Expansion));
|
||||
|
||||
if (compressor.option("reduce_vars")
|
||||
&& fn instanceof AST_SymbolRef
|
||||
&& !has_annotation(self, _NOINLINE)
|
||||
) {
|
||||
const fixed = fn.fixed_value();
|
||||
|
||||
if (
|
||||
retain_top_func(fixed, compressor)
|
||||
|| !compressor.toplevel.funcs && exp.definition().global
|
||||
) {
|
||||
return self;
|
||||
}
|
||||
|
||||
fn = fixed;
|
||||
}
|
||||
|
||||
var is_func = fn instanceof AST_Lambda;
|
||||
|
||||
var stat = is_func && fn.body[0];
|
||||
var is_regular_func = is_func && !fn.is_generator && !fn.async;
|
||||
var can_inline = is_regular_func && compressor.option("inline") && !self.is_callee_pure(compressor);
|
||||
if (can_inline && stat instanceof AST_Return) {
|
||||
let returned = stat.value;
|
||||
if (!returned || returned.is_constant_expression()) {
|
||||
if (returned) {
|
||||
returned = returned.clone(true);
|
||||
} else {
|
||||
returned = make_node(AST_Undefined, self);
|
||||
}
|
||||
const args = self.args.concat(returned);
|
||||
return make_sequence(self, args).optimize(compressor);
|
||||
}
|
||||
|
||||
// optimize identity function
|
||||
if (
|
||||
fn.argnames.length === 1
|
||||
&& (fn.argnames[0] instanceof AST_SymbolFunarg)
|
||||
&& self.args.length < 2
|
||||
&& !(self.args[0] instanceof AST_Expansion)
|
||||
&& returned instanceof AST_SymbolRef
|
||||
&& returned.name === fn.argnames[0].name
|
||||
) {
|
||||
const replacement =
|
||||
(self.args[0] || make_node(AST_Undefined)).optimize(compressor);
|
||||
|
||||
let parent;
|
||||
if (
|
||||
replacement instanceof AST_PropAccess
|
||||
&& (parent = compressor.parent()) instanceof AST_Call
|
||||
&& parent.expression === self
|
||||
) {
|
||||
// identity function was being used to remove `this`, like in
|
||||
//
|
||||
// id(bag.no_this)(...)
|
||||
//
|
||||
// Replace with a larger but more effish (0, bag.no_this) wrapper.
|
||||
|
||||
return make_sequence(self, [
|
||||
make_node(AST_Number, self, { value: 0 }),
|
||||
replacement
|
||||
]);
|
||||
}
|
||||
// replace call with first argument or undefined if none passed
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
|
||||
if (can_inline) {
|
||||
var scope, in_loop, level = -1;
|
||||
let def;
|
||||
let returned_value;
|
||||
let nearest_scope;
|
||||
if (simple_args
|
||||
&& !fn.uses_arguments
|
||||
&& !(compressor.parent() instanceof AST_Class)
|
||||
&& !(fn.name && fn instanceof AST_Function)
|
||||
&& (returned_value = can_flatten_body(stat))
|
||||
&& (exp === fn
|
||||
|| has_annotation(self, _INLINE)
|
||||
|| compressor.option("unused")
|
||||
&& (def = exp.definition()).references.length == 1
|
||||
&& !is_recursive_ref(compressor, def)
|
||||
&& fn.is_constant_expression(exp.scope))
|
||||
&& !has_annotation(self, _PURE | _NOINLINE)
|
||||
&& !fn.contains_this()
|
||||
&& can_inject_symbols()
|
||||
&& (nearest_scope = compressor.find_scope())
|
||||
&& !scope_encloses_variables_in_this_scope(nearest_scope, fn)
|
||||
&& !(function in_default_assign() {
|
||||
// Due to the fact function parameters have their own scope
|
||||
// which can't use `var something` in the function body within,
|
||||
// we simply don't inline into DefaultAssign.
|
||||
let i = 0;
|
||||
let p;
|
||||
while ((p = compressor.parent(i++))) {
|
||||
if (p instanceof AST_DefaultAssign) return true;
|
||||
if (p instanceof AST_Block) break;
|
||||
}
|
||||
return false;
|
||||
})()
|
||||
&& !(scope instanceof AST_Class)
|
||||
) {
|
||||
set_flag(fn, SQUEEZED);
|
||||
nearest_scope.add_child_scope(fn);
|
||||
return make_sequence(self, flatten_fn(returned_value)).optimize(compressor);
|
||||
}
|
||||
}
|
||||
|
||||
if (can_inline && has_annotation(self, _INLINE)) {
|
||||
set_flag(fn, SQUEEZED);
|
||||
fn = make_node(fn.CTOR === AST_Defun ? AST_Function : fn.CTOR, fn, fn);
|
||||
fn = fn.clone(true);
|
||||
fn.figure_out_scope({}, {
|
||||
parent_scope: compressor.find_scope(),
|
||||
toplevel: compressor.get_toplevel()
|
||||
});
|
||||
|
||||
return make_node(AST_Call, self, {
|
||||
expression: fn,
|
||||
args: self.args,
|
||||
}).optimize(compressor);
|
||||
}
|
||||
|
||||
const can_drop_this_call = is_regular_func && compressor.option("side_effects") && fn.body.every(is_empty);
|
||||
if (can_drop_this_call) {
|
||||
var args = self.args.concat(make_node(AST_Undefined, self));
|
||||
return make_sequence(self, args).optimize(compressor);
|
||||
}
|
||||
|
||||
if (compressor.option("negate_iife")
|
||||
&& compressor.parent() instanceof AST_SimpleStatement
|
||||
&& is_iife_call(self)) {
|
||||
return self.negate(compressor, true);
|
||||
}
|
||||
|
||||
var ev = self.evaluate(compressor);
|
||||
if (ev !== self) {
|
||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
||||
return best_of(compressor, ev, self);
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
function return_value(stat) {
|
||||
if (!stat) return make_node(AST_Undefined, self);
|
||||
if (stat instanceof AST_Return) {
|
||||
if (!stat.value) return make_node(AST_Undefined, self);
|
||||
return stat.value.clone(true);
|
||||
}
|
||||
if (stat instanceof AST_SimpleStatement) {
|
||||
return make_node(AST_UnaryPrefix, stat, {
|
||||
operator: "void",
|
||||
expression: stat.body.clone(true)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function can_flatten_body(stat) {
|
||||
var body = fn.body;
|
||||
var len = body.length;
|
||||
if (compressor.option("inline") < 3) {
|
||||
return len == 1 && return_value(stat);
|
||||
}
|
||||
stat = null;
|
||||
for (var i = 0; i < len; i++) {
|
||||
var line = body[i];
|
||||
if (line instanceof AST_Var) {
|
||||
if (stat && !line.definitions.every((var_def) =>
|
||||
!var_def.value
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
} else if (stat) {
|
||||
return false;
|
||||
} else if (!(line instanceof AST_EmptyStatement)) {
|
||||
stat = line;
|
||||
}
|
||||
}
|
||||
return return_value(stat);
|
||||
}
|
||||
|
||||
function can_inject_args(block_scoped, safe_to_inject) {
|
||||
for (var i = 0, len = fn.argnames.length; i < len; i++) {
|
||||
var arg = fn.argnames[i];
|
||||
if (arg instanceof AST_DefaultAssign) {
|
||||
if (has_flag(arg.left, UNUSED)) continue;
|
||||
return false;
|
||||
}
|
||||
if (arg instanceof AST_Destructuring) return false;
|
||||
if (arg instanceof AST_Expansion) {
|
||||
if (has_flag(arg.expression, UNUSED)) continue;
|
||||
return false;
|
||||
}
|
||||
if (has_flag(arg, UNUSED)) continue;
|
||||
if (!safe_to_inject
|
||||
|| block_scoped.has(arg.name)
|
||||
|| identifier_atom.has(arg.name)
|
||||
|| scope.conflicting_def(arg.name)) {
|
||||
return false;
|
||||
}
|
||||
if (in_loop) in_loop.push(arg.definition());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function can_inject_vars(block_scoped, safe_to_inject) {
|
||||
var len = fn.body.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
var stat = fn.body[i];
|
||||
if (!(stat instanceof AST_Var)) continue;
|
||||
if (!safe_to_inject) return false;
|
||||
for (var j = stat.definitions.length; --j >= 0;) {
|
||||
var name = stat.definitions[j].name;
|
||||
if (name instanceof AST_Destructuring
|
||||
|| block_scoped.has(name.name)
|
||||
|| identifier_atom.has(name.name)
|
||||
|| scope.conflicting_def(name.name)) {
|
||||
return false;
|
||||
}
|
||||
if (in_loop) in_loop.push(name.definition());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function can_inject_symbols() {
|
||||
var block_scoped = new Set();
|
||||
do {
|
||||
scope = compressor.parent(++level);
|
||||
if (scope.is_block_scope() && scope.block_scope) {
|
||||
// TODO this is sometimes undefined during compression.
|
||||
// But it should always have a value!
|
||||
scope.block_scope.variables.forEach(function (variable) {
|
||||
block_scoped.add(variable.name);
|
||||
});
|
||||
}
|
||||
if (scope instanceof AST_Catch) {
|
||||
// TODO can we delete? AST_Catch is a block scope.
|
||||
if (scope.argname) {
|
||||
block_scoped.add(scope.argname.name);
|
||||
}
|
||||
} else if (scope instanceof AST_IterationStatement) {
|
||||
in_loop = [];
|
||||
} else if (scope instanceof AST_SymbolRef) {
|
||||
if (scope.fixed_value() instanceof AST_Scope) return false;
|
||||
}
|
||||
} while (!(scope instanceof AST_Scope));
|
||||
|
||||
var safe_to_inject = !(scope instanceof AST_Toplevel) || compressor.toplevel.vars;
|
||||
var inline = compressor.option("inline");
|
||||
if (!can_inject_vars(block_scoped, inline >= 3 && safe_to_inject)) return false;
|
||||
if (!can_inject_args(block_scoped, inline >= 2 && safe_to_inject)) return false;
|
||||
return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
|
||||
}
|
||||
|
||||
function append_var(decls, expressions, name, value) {
|
||||
var def = name.definition();
|
||||
|
||||
// Name already exists, only when a function argument had the same name
|
||||
const already_appended = scope.variables.has(name.name);
|
||||
if (!already_appended) {
|
||||
scope.variables.set(name.name, def);
|
||||
scope.enclosed.push(def);
|
||||
decls.push(make_node(AST_VarDef, name, {
|
||||
name: name,
|
||||
value: null
|
||||
}));
|
||||
}
|
||||
|
||||
var sym = make_node(AST_SymbolRef, name, name);
|
||||
def.references.push(sym);
|
||||
if (value) expressions.push(make_node(AST_Assign, self, {
|
||||
operator: "=",
|
||||
logical: false,
|
||||
left: sym,
|
||||
right: value.clone()
|
||||
}));
|
||||
}
|
||||
|
||||
function flatten_args(decls, expressions) {
|
||||
var len = fn.argnames.length;
|
||||
for (var i = self.args.length; --i >= len;) {
|
||||
expressions.push(self.args[i]);
|
||||
}
|
||||
for (i = len; --i >= 0;) {
|
||||
var name = fn.argnames[i];
|
||||
var value = self.args[i];
|
||||
if (has_flag(name, UNUSED) || !name.name || scope.conflicting_def(name.name)) {
|
||||
if (value) expressions.push(value);
|
||||
} else {
|
||||
var symbol = make_node(AST_SymbolVar, name, name);
|
||||
name.definition().orig.push(symbol);
|
||||
if (!value && in_loop) value = make_node(AST_Undefined, self);
|
||||
append_var(decls, expressions, symbol, value);
|
||||
}
|
||||
}
|
||||
decls.reverse();
|
||||
expressions.reverse();
|
||||
}
|
||||
|
||||
function flatten_vars(decls, expressions) {
|
||||
var pos = expressions.length;
|
||||
for (var i = 0, lines = fn.body.length; i < lines; i++) {
|
||||
var stat = fn.body[i];
|
||||
if (!(stat instanceof AST_Var)) continue;
|
||||
for (var j = 0, defs = stat.definitions.length; j < defs; j++) {
|
||||
var var_def = stat.definitions[j];
|
||||
var name = var_def.name;
|
||||
append_var(decls, expressions, name, var_def.value);
|
||||
if (in_loop && fn.argnames.every((argname) =>
|
||||
argname.name != name.name
|
||||
)) {
|
||||
var def = fn.variables.get(name.name);
|
||||
var sym = make_node(AST_SymbolRef, name, name);
|
||||
def.references.push(sym);
|
||||
expressions.splice(pos++, 0, make_node(AST_Assign, var_def, {
|
||||
operator: "=",
|
||||
logical: false,
|
||||
left: sym,
|
||||
right: make_node(AST_Undefined, name)
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function flatten_fn(returned_value) {
|
||||
var decls = [];
|
||||
var expressions = [];
|
||||
flatten_args(decls, expressions);
|
||||
flatten_vars(decls, expressions);
|
||||
expressions.push(returned_value);
|
||||
|
||||
if (decls.length) {
|
||||
const i = scope.body.indexOf(compressor.parent(level - 1)) + 1;
|
||||
scope.body.splice(i, 0, make_node(AST_Var, fn, {
|
||||
definitions: decls
|
||||
}));
|
||||
}
|
||||
|
||||
return expressions.map(exp => exp.clone(true));
|
||||
}
|
||||
}
|
206
my-app/node_modules/terser/lib/compress/native-objects.js
generated
vendored
Executable file
206
my-app/node_modules/terser/lib/compress/native-objects.js
generated
vendored
Executable file
|
@ -0,0 +1,206 @@
|
|||
/***********************************************************************
|
||||
|
||||
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||
https://github.com/mishoo/UglifyJS2
|
||||
|
||||
-------------------------------- (C) ---------------------------------
|
||||
|
||||
Author: Mihai Bazon
|
||||
<mihai.bazon@gmail.com>
|
||||
http://mihai.bazon.net/blog
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
import { makePredicate } from "../utils/index.js";
|
||||
|
||||
// Lists of native methods, useful for `unsafe` option which assumes they exist.
|
||||
// Note: Lots of methods and functions are missing here, in case they aren't pure
|
||||
// or not available in all JS environments.
|
||||
|
||||
function make_nested_lookup(obj) {
|
||||
const out = new Map();
|
||||
for (var key of Object.keys(obj)) {
|
||||
out.set(key, makePredicate(obj[key]));
|
||||
}
|
||||
|
||||
const does_have = (global_name, fname) => {
|
||||
const inner_map = out.get(global_name);
|
||||
return inner_map != null && inner_map.has(fname);
|
||||
};
|
||||
return does_have;
|
||||
}
|
||||
|
||||
// Objects which are safe to access without throwing or causing a side effect.
|
||||
// Usually we'd check the `unsafe` option first but these are way too common for that
|
||||
export const pure_prop_access_globals = new Set([
|
||||
"Number",
|
||||
"String",
|
||||
"Array",
|
||||
"Object",
|
||||
"Function",
|
||||
"Promise",
|
||||
]);
|
||||
|
||||
const object_methods = [
|
||||
"constructor",
|
||||
"toString",
|
||||
"valueOf",
|
||||
];
|
||||
|
||||
export const is_pure_native_method = make_nested_lookup({
|
||||
Array: [
|
||||
"at",
|
||||
"flat",
|
||||
"includes",
|
||||
"indexOf",
|
||||
"join",
|
||||
"lastIndexOf",
|
||||
"slice",
|
||||
...object_methods,
|
||||
],
|
||||
Boolean: object_methods,
|
||||
Function: object_methods,
|
||||
Number: [
|
||||
"toExponential",
|
||||
"toFixed",
|
||||
"toPrecision",
|
||||
...object_methods,
|
||||
],
|
||||
Object: object_methods,
|
||||
RegExp: [
|
||||
"test",
|
||||
...object_methods,
|
||||
],
|
||||
String: [
|
||||
"at",
|
||||
"charAt",
|
||||
"charCodeAt",
|
||||
"charPointAt",
|
||||
"concat",
|
||||
"endsWith",
|
||||
"fromCharCode",
|
||||
"fromCodePoint",
|
||||
"includes",
|
||||
"indexOf",
|
||||
"italics",
|
||||
"lastIndexOf",
|
||||
"localeCompare",
|
||||
"match",
|
||||
"matchAll",
|
||||
"normalize",
|
||||
"padStart",
|
||||
"padEnd",
|
||||
"repeat",
|
||||
"replace",
|
||||
"replaceAll",
|
||||
"search",
|
||||
"slice",
|
||||
"split",
|
||||
"startsWith",
|
||||
"substr",
|
||||
"substring",
|
||||
"repeat",
|
||||
"toLocaleLowerCase",
|
||||
"toLocaleUpperCase",
|
||||
"toLowerCase",
|
||||
"toUpperCase",
|
||||
"trim",
|
||||
"trimEnd",
|
||||
"trimStart",
|
||||
...object_methods,
|
||||
],
|
||||
});
|
||||
|
||||
export const is_pure_native_fn = make_nested_lookup({
|
||||
Array: [
|
||||
"isArray",
|
||||
],
|
||||
Math: [
|
||||
"abs",
|
||||
"acos",
|
||||
"asin",
|
||||
"atan",
|
||||
"ceil",
|
||||
"cos",
|
||||
"exp",
|
||||
"floor",
|
||||
"log",
|
||||
"round",
|
||||
"sin",
|
||||
"sqrt",
|
||||
"tan",
|
||||
"atan2",
|
||||
"pow",
|
||||
"max",
|
||||
"min",
|
||||
],
|
||||
Number: [
|
||||
"isFinite",
|
||||
"isNaN",
|
||||
],
|
||||
Object: [
|
||||
"create",
|
||||
"getOwnPropertyDescriptor",
|
||||
"getOwnPropertyNames",
|
||||
"getPrototypeOf",
|
||||
"isExtensible",
|
||||
"isFrozen",
|
||||
"isSealed",
|
||||
"hasOwn",
|
||||
"keys",
|
||||
],
|
||||
String: [
|
||||
"fromCharCode",
|
||||
],
|
||||
});
|
||||
|
||||
// Known numeric values which come with JS environments
|
||||
export const is_pure_native_value = make_nested_lookup({
|
||||
Math: [
|
||||
"E",
|
||||
"LN10",
|
||||
"LN2",
|
||||
"LOG2E",
|
||||
"LOG10E",
|
||||
"PI",
|
||||
"SQRT1_2",
|
||||
"SQRT2",
|
||||
],
|
||||
Number: [
|
||||
"MAX_VALUE",
|
||||
"MIN_VALUE",
|
||||
"NaN",
|
||||
"NEGATIVE_INFINITY",
|
||||
"POSITIVE_INFINITY",
|
||||
],
|
||||
});
|
824
my-app/node_modules/terser/lib/compress/reduce-vars.js
generated
vendored
Executable file
824
my-app/node_modules/terser/lib/compress/reduce-vars.js
generated
vendored
Executable file
|
@ -0,0 +1,824 @@
|
|||
/***********************************************************************
|
||||
|
||||
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||
https://github.com/mishoo/UglifyJS2
|
||||
|
||||
-------------------------------- (C) ---------------------------------
|
||||
|
||||
Author: Mihai Bazon
|
||||
<mihai.bazon@gmail.com>
|
||||
http://mihai.bazon.net/blog
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
import {
|
||||
AST_Accessor,
|
||||
AST_Array,
|
||||
AST_Assign,
|
||||
AST_Await,
|
||||
AST_Binary,
|
||||
AST_Block,
|
||||
AST_Call,
|
||||
AST_Case,
|
||||
AST_Chain,
|
||||
AST_Class,
|
||||
AST_ClassStaticBlock,
|
||||
AST_ClassExpression,
|
||||
AST_Conditional,
|
||||
AST_Default,
|
||||
AST_Defun,
|
||||
AST_Destructuring,
|
||||
AST_Do,
|
||||
AST_Exit,
|
||||
AST_Expansion,
|
||||
AST_For,
|
||||
AST_ForIn,
|
||||
AST_If,
|
||||
AST_LabeledStatement,
|
||||
AST_Lambda,
|
||||
AST_New,
|
||||
AST_Node,
|
||||
AST_Number,
|
||||
AST_ObjectKeyVal,
|
||||
AST_PropAccess,
|
||||
AST_Scope,
|
||||
AST_Sequence,
|
||||
AST_SimpleStatement,
|
||||
AST_Symbol,
|
||||
AST_SymbolCatch,
|
||||
AST_SymbolConst,
|
||||
AST_SymbolDeclaration,
|
||||
AST_SymbolDefun,
|
||||
AST_SymbolFunarg,
|
||||
AST_SymbolLambda,
|
||||
AST_SymbolRef,
|
||||
AST_This,
|
||||
AST_Toplevel,
|
||||
AST_Try,
|
||||
AST_Unary,
|
||||
AST_UnaryPrefix,
|
||||
AST_Undefined,
|
||||
AST_VarDef,
|
||||
AST_While,
|
||||
AST_Yield,
|
||||
|
||||
walk,
|
||||
walk_body,
|
||||
|
||||
TreeWalker,
|
||||
} from "../ast.js";
|
||||
import { HOP, make_node, noop } from "../utils/index.js";
|
||||
|
||||
import { lazy_op, is_modified, is_lhs } from "./inference.js";
|
||||
import { INLINED, clear_flag } from "./compressor-flags.js";
|
||||
import { read_property, has_break_or_continue, is_recursive_ref } from "./common.js";
|
||||
|
||||
/**
|
||||
* Define the method AST_Node#reduce_vars, which goes through the AST in
|
||||
* execution order to perform basic flow analysis
|
||||
*/
|
||||
function def_reduce_vars(node, func) {
|
||||
node.DEFMETHOD("reduce_vars", func);
|
||||
}
|
||||
|
||||
def_reduce_vars(AST_Node, noop);
|
||||
|
||||
/** Clear definition properties */
|
||||
function reset_def(compressor, def) {
|
||||
def.assignments = 0;
|
||||
def.chained = false;
|
||||
def.direct_access = false;
|
||||
def.escaped = 0;
|
||||
def.recursive_refs = 0;
|
||||
def.references = [];
|
||||
def.single_use = undefined;
|
||||
if (
|
||||
def.scope.pinned()
|
||||
|| (def.orig[0] instanceof AST_SymbolFunarg && def.scope.uses_arguments)
|
||||
) {
|
||||
def.fixed = false;
|
||||
} else if (def.orig[0] instanceof AST_SymbolConst || !compressor.exposed(def)) {
|
||||
def.fixed = def.init;
|
||||
} else {
|
||||
def.fixed = false;
|
||||
}
|
||||
}
|
||||
|
||||
function reset_variables(tw, compressor, node) {
|
||||
node.variables.forEach(function(def) {
|
||||
reset_def(compressor, def);
|
||||
if (def.fixed === null) {
|
||||
tw.defs_to_safe_ids.set(def.id, tw.safe_ids);
|
||||
mark(tw, def, true);
|
||||
} else if (def.fixed) {
|
||||
tw.loop_ids.set(def.id, tw.in_loop);
|
||||
mark(tw, def, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function reset_block_variables(compressor, node) {
|
||||
if (node.block_scope) node.block_scope.variables.forEach((def) => {
|
||||
reset_def(compressor, def);
|
||||
});
|
||||
}
|
||||
|
||||
function push(tw) {
|
||||
tw.safe_ids = Object.create(tw.safe_ids);
|
||||
}
|
||||
|
||||
function pop(tw) {
|
||||
tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
|
||||
}
|
||||
|
||||
function mark(tw, def, safe) {
|
||||
tw.safe_ids[def.id] = safe;
|
||||
}
|
||||
|
||||
function safe_to_read(tw, def) {
|
||||
if (def.single_use == "m") return false;
|
||||
if (tw.safe_ids[def.id]) {
|
||||
if (def.fixed == null) {
|
||||
var orig = def.orig[0];
|
||||
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
|
||||
def.fixed = make_node(AST_Undefined, orig);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return def.fixed instanceof AST_Defun;
|
||||
}
|
||||
|
||||
function safe_to_assign(tw, def, scope, value) {
|
||||
if (def.fixed === undefined) return true;
|
||||
let def_safe_ids;
|
||||
if (def.fixed === null
|
||||
&& (def_safe_ids = tw.defs_to_safe_ids.get(def.id))
|
||||
) {
|
||||
def_safe_ids[def.id] = false;
|
||||
tw.defs_to_safe_ids.delete(def.id);
|
||||
return true;
|
||||
}
|
||||
if (!HOP(tw.safe_ids, def.id)) return false;
|
||||
if (!safe_to_read(tw, def)) return false;
|
||||
if (def.fixed === false) return false;
|
||||
if (def.fixed != null && (!value || def.references.length > def.assignments)) return false;
|
||||
if (def.fixed instanceof AST_Defun) {
|
||||
return value instanceof AST_Node && def.fixed.parent_scope === scope;
|
||||
}
|
||||
return def.orig.every((sym) => {
|
||||
return !(sym instanceof AST_SymbolConst
|
||||
|| sym instanceof AST_SymbolDefun
|
||||
|| sym instanceof AST_SymbolLambda);
|
||||
});
|
||||
}
|
||||
|
||||
function ref_once(tw, compressor, def) {
|
||||
return compressor.option("unused")
|
||||
&& !def.scope.pinned()
|
||||
&& def.references.length - def.recursive_refs == 1
|
||||
&& tw.loop_ids.get(def.id) === tw.in_loop;
|
||||
}
|
||||
|
||||
function is_immutable(value) {
|
||||
if (!value) return false;
|
||||
return value.is_constant()
|
||||
|| value instanceof AST_Lambda
|
||||
|| value instanceof AST_This;
|
||||
}
|
||||
|
||||
// A definition "escapes" when its value can leave the point of use.
|
||||
// Example: `a = b || c`
|
||||
// In this example, "b" and "c" are escaping, because they're going into "a"
|
||||
//
|
||||
// def.escaped is != 0 when it escapes.
|
||||
//
|
||||
// When greater than 1, it means that N chained properties will be read off
|
||||
// of that def before an escape occurs. This is useful for evaluating
|
||||
// property accesses, where you need to know when to stop.
|
||||
function mark_escaped(tw, d, scope, node, value, level = 0, depth = 1) {
|
||||
var parent = tw.parent(level);
|
||||
if (value) {
|
||||
if (value.is_constant()) return;
|
||||
if (value instanceof AST_ClassExpression) return;
|
||||
}
|
||||
|
||||
if (
|
||||
parent instanceof AST_Assign && (parent.operator === "=" || parent.logical) && node === parent.right
|
||||
|| parent instanceof AST_Call && (node !== parent.expression || parent instanceof AST_New)
|
||||
|| parent instanceof AST_Exit && node === parent.value && node.scope !== d.scope
|
||||
|| parent instanceof AST_VarDef && node === parent.value
|
||||
|| parent instanceof AST_Yield && node === parent.value && node.scope !== d.scope
|
||||
) {
|
||||
if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
|
||||
if (!d.escaped || d.escaped > depth) d.escaped = depth;
|
||||
return;
|
||||
} else if (
|
||||
parent instanceof AST_Array
|
||||
|| parent instanceof AST_Await
|
||||
|| parent instanceof AST_Binary && lazy_op.has(parent.operator)
|
||||
|| parent instanceof AST_Conditional && node !== parent.condition
|
||||
|| parent instanceof AST_Expansion
|
||||
|| parent instanceof AST_Sequence && node === parent.tail_node()
|
||||
) {
|
||||
mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
|
||||
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
|
||||
var obj = tw.parent(level + 1);
|
||||
|
||||
mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
|
||||
} else if (parent instanceof AST_PropAccess && node === parent.expression) {
|
||||
value = read_property(value, parent.property);
|
||||
|
||||
mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
|
||||
if (value) return;
|
||||
}
|
||||
|
||||
if (level > 0) return;
|
||||
if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
|
||||
if (parent instanceof AST_SimpleStatement) return;
|
||||
|
||||
d.direct_access = true;
|
||||
}
|
||||
|
||||
const suppress = node => walk(node, node => {
|
||||
if (!(node instanceof AST_Symbol)) return;
|
||||
var d = node.definition();
|
||||
if (!d) return;
|
||||
if (node instanceof AST_SymbolRef) d.references.push(node);
|
||||
d.fixed = false;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Accessor, function(tw, descend, compressor) {
|
||||
push(tw);
|
||||
reset_variables(tw, compressor, this);
|
||||
descend();
|
||||
pop(tw);
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Assign, function(tw, descend, compressor) {
|
||||
var node = this;
|
||||
if (node.left instanceof AST_Destructuring) {
|
||||
suppress(node.left);
|
||||
return;
|
||||
}
|
||||
|
||||
const finish_walk = () => {
|
||||
if (node.logical) {
|
||||
node.left.walk(tw);
|
||||
|
||||
push(tw);
|
||||
node.right.walk(tw);
|
||||
pop(tw);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var sym = node.left;
|
||||
if (!(sym instanceof AST_SymbolRef)) return finish_walk();
|
||||
|
||||
var def = sym.definition();
|
||||
var safe = safe_to_assign(tw, def, sym.scope, node.right);
|
||||
def.assignments++;
|
||||
if (!safe) return finish_walk();
|
||||
|
||||
var fixed = def.fixed;
|
||||
if (!fixed && node.operator != "=" && !node.logical) return finish_walk();
|
||||
|
||||
var eq = node.operator == "=";
|
||||
var value = eq ? node.right : node;
|
||||
if (is_modified(compressor, tw, node, value, 0)) return finish_walk();
|
||||
|
||||
def.references.push(sym);
|
||||
|
||||
if (!node.logical) {
|
||||
if (!eq) def.chained = true;
|
||||
|
||||
def.fixed = eq ? function() {
|
||||
return node.right;
|
||||
} : function() {
|
||||
return make_node(AST_Binary, node, {
|
||||
operator: node.operator.slice(0, -1),
|
||||
left: fixed instanceof AST_Node ? fixed : fixed(),
|
||||
right: node.right
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (node.logical) {
|
||||
mark(tw, def, false);
|
||||
push(tw);
|
||||
node.right.walk(tw);
|
||||
pop(tw);
|
||||
return true;
|
||||
}
|
||||
|
||||
mark(tw, def, false);
|
||||
node.right.walk(tw);
|
||||
mark(tw, def, true);
|
||||
|
||||
mark_escaped(tw, def, sym.scope, node, value, 0, 1);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Binary, function(tw) {
|
||||
if (!lazy_op.has(this.operator)) return;
|
||||
this.left.walk(tw);
|
||||
push(tw);
|
||||
this.right.walk(tw);
|
||||
pop(tw);
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Block, function(tw, descend, compressor) {
|
||||
reset_block_variables(compressor, this);
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Case, function(tw) {
|
||||
push(tw);
|
||||
this.expression.walk(tw);
|
||||
pop(tw);
|
||||
push(tw);
|
||||
walk_body(this, tw);
|
||||
pop(tw);
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Class, function(tw, descend) {
|
||||
clear_flag(this, INLINED);
|
||||
push(tw);
|
||||
descend();
|
||||
pop(tw);
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_ClassStaticBlock, function(tw, descend, compressor) {
|
||||
reset_block_variables(compressor, this);
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Conditional, function(tw) {
|
||||
this.condition.walk(tw);
|
||||
push(tw);
|
||||
this.consequent.walk(tw);
|
||||
pop(tw);
|
||||
push(tw);
|
||||
this.alternative.walk(tw);
|
||||
pop(tw);
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Chain, function(tw, descend) {
|
||||
// Chains' conditions apply left-to-right, cumulatively.
|
||||
// If we walk normally we don't go in that order because we would pop before pushing again
|
||||
// Solution: AST_PropAccess and AST_Call push when they are optional, and never pop.
|
||||
// Then we pop everything when they are done being walked.
|
||||
const safe_ids = tw.safe_ids;
|
||||
|
||||
descend();
|
||||
|
||||
// Unroll back to start
|
||||
tw.safe_ids = safe_ids;
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Call, function (tw) {
|
||||
this.expression.walk(tw);
|
||||
|
||||
if (this.optional) {
|
||||
// Never pop -- it's popped at AST_Chain above
|
||||
push(tw);
|
||||
}
|
||||
|
||||
for (const arg of this.args) arg.walk(tw);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_PropAccess, function (tw) {
|
||||
if (!this.optional) return;
|
||||
|
||||
this.expression.walk(tw);
|
||||
|
||||
// Never pop -- it's popped at AST_Chain above
|
||||
push(tw);
|
||||
|
||||
if (this.property instanceof AST_Node) this.property.walk(tw);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Default, function(tw, descend) {
|
||||
push(tw);
|
||||
descend();
|
||||
pop(tw);
|
||||
return true;
|
||||
});
|
||||
|
||||
function mark_lambda(tw, descend, compressor) {
|
||||
clear_flag(this, INLINED);
|
||||
push(tw);
|
||||
reset_variables(tw, compressor, this);
|
||||
|
||||
var iife;
|
||||
if (!this.name
|
||||
&& !this.uses_arguments
|
||||
&& !this.pinned()
|
||||
&& (iife = tw.parent()) instanceof AST_Call
|
||||
&& iife.expression === this
|
||||
&& !iife.args.some(arg => arg instanceof AST_Expansion)
|
||||
&& this.argnames.every(arg_name => arg_name instanceof AST_Symbol)
|
||||
) {
|
||||
// Virtually turn IIFE parameters into variable definitions:
|
||||
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
|
||||
// So existing transformation rules can work on them.
|
||||
this.argnames.forEach((arg, i) => {
|
||||
if (!arg.definition) return;
|
||||
var d = arg.definition();
|
||||
// Avoid setting fixed when there's more than one origin for a variable value
|
||||
if (d.orig.length > 1) return;
|
||||
if (d.fixed === undefined && (!this.uses_arguments || tw.has_directive("use strict"))) {
|
||||
d.fixed = function() {
|
||||
return iife.args[i] || make_node(AST_Undefined, iife);
|
||||
};
|
||||
tw.loop_ids.set(d.id, tw.in_loop);
|
||||
mark(tw, d, true);
|
||||
} else {
|
||||
d.fixed = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
descend();
|
||||
pop(tw);
|
||||
|
||||
handle_defined_after_hoist(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* It's possible for a hoisted function to use something that's not defined yet. Example:
|
||||
*
|
||||
* hoisted();
|
||||
* var defined_after = true;
|
||||
* function hoisted() {
|
||||
* // use defined_after
|
||||
* }
|
||||
*
|
||||
* This function is called on the parent to handle this issue.
|
||||
*/
|
||||
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_Scope
|
||||
|| node instanceof AST_SimpleStatement
|
||||
) return true;
|
||||
});
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
for (const def of defun.enclosed) {
|
||||
if (
|
||||
def.fixed === false
|
||||
|| def === fname_def
|
||||
|| def.scope.get_defun_scope() !== parent
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// defun is hoisted, so always safe
|
||||
if (
|
||||
def.assignments === 0
|
||||
&& def.orig.length === 1
|
||||
&& def.orig[0] instanceof AST_SymbolDefun
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (found_self_ref_in_other_defuns) {
|
||||
def.fixed = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// for the slower checks below this loop
|
||||
potential_conflicts.push({ defun, def, fname_def });
|
||||
symbols_of_interest.add(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();
|
||||
|
||||
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;
|
||||
|
||||
defun_ranges.set(node, { start, end });
|
||||
return true;
|
||||
}
|
||||
// if we found a defun on the list, mark IN_DEFUN=id and descend
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
})));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
};
|
||||
|
||||
const read_defun_at = find(fname_def.id);
|
||||
const wrote_def_at = find(def.id, read_defun_at + 1, true);
|
||||
|
||||
const wrote_def_after_reading_defun = read_defun_at != -1 && wrote_def_at != -1 && wrote_def_at > read_defun_at;
|
||||
|
||||
if (wrote_def_after_reading_defun) {
|
||||
def.fixed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def_reduce_vars(AST_Lambda, mark_lambda);
|
||||
|
||||
def_reduce_vars(AST_Do, function(tw, descend, compressor) {
|
||||
reset_block_variables(compressor, this);
|
||||
const saved_loop = tw.in_loop;
|
||||
tw.in_loop = this;
|
||||
push(tw);
|
||||
this.body.walk(tw);
|
||||
if (has_break_or_continue(this)) {
|
||||
pop(tw);
|
||||
push(tw);
|
||||
}
|
||||
this.condition.walk(tw);
|
||||
pop(tw);
|
||||
tw.in_loop = saved_loop;
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_For, function(tw, descend, compressor) {
|
||||
reset_block_variables(compressor, this);
|
||||
if (this.init) this.init.walk(tw);
|
||||
const saved_loop = tw.in_loop;
|
||||
tw.in_loop = this;
|
||||
push(tw);
|
||||
if (this.condition) this.condition.walk(tw);
|
||||
this.body.walk(tw);
|
||||
if (this.step) {
|
||||
if (has_break_or_continue(this)) {
|
||||
pop(tw);
|
||||
push(tw);
|
||||
}
|
||||
this.step.walk(tw);
|
||||
}
|
||||
pop(tw);
|
||||
tw.in_loop = saved_loop;
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_ForIn, function(tw, descend, compressor) {
|
||||
reset_block_variables(compressor, this);
|
||||
suppress(this.init);
|
||||
this.object.walk(tw);
|
||||
const saved_loop = tw.in_loop;
|
||||
tw.in_loop = this;
|
||||
push(tw);
|
||||
this.body.walk(tw);
|
||||
pop(tw);
|
||||
tw.in_loop = saved_loop;
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_If, function(tw) {
|
||||
this.condition.walk(tw);
|
||||
push(tw);
|
||||
this.body.walk(tw);
|
||||
pop(tw);
|
||||
if (this.alternative) {
|
||||
push(tw);
|
||||
this.alternative.walk(tw);
|
||||
pop(tw);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_LabeledStatement, function(tw) {
|
||||
push(tw);
|
||||
this.body.walk(tw);
|
||||
pop(tw);
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_SymbolCatch, function() {
|
||||
this.definition().fixed = false;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_SymbolRef, function(tw, descend, compressor) {
|
||||
var d = this.definition();
|
||||
d.references.push(this);
|
||||
if (d.references.length == 1
|
||||
&& !d.fixed
|
||||
&& d.orig[0] instanceof AST_SymbolDefun) {
|
||||
tw.loop_ids.set(d.id, tw.in_loop);
|
||||
}
|
||||
var fixed_value;
|
||||
if (d.fixed === undefined || !safe_to_read(tw, d)) {
|
||||
d.fixed = false;
|
||||
} else if (d.fixed) {
|
||||
fixed_value = this.fixed_value();
|
||||
if (
|
||||
fixed_value instanceof AST_Lambda
|
||||
&& is_recursive_ref(tw, d)
|
||||
) {
|
||||
d.recursive_refs++;
|
||||
} else if (fixed_value
|
||||
&& !compressor.exposed(d)
|
||||
&& ref_once(tw, compressor, d)
|
||||
) {
|
||||
d.single_use =
|
||||
fixed_value instanceof AST_Lambda && !fixed_value.pinned()
|
||||
|| fixed_value instanceof AST_Class
|
||||
|| d.scope === this.scope && fixed_value.is_constant_expression();
|
||||
} else {
|
||||
d.single_use = false;
|
||||
}
|
||||
if (is_modified(compressor, tw, this, fixed_value, 0, is_immutable(fixed_value))) {
|
||||
if (d.single_use) {
|
||||
d.single_use = "m";
|
||||
} else {
|
||||
d.fixed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
mark_escaped(tw, d, this.scope, this, fixed_value, 0, 1);
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Toplevel, function(tw, descend, compressor) {
|
||||
this.globals.forEach(function(def) {
|
||||
reset_def(compressor, def);
|
||||
});
|
||||
reset_variables(tw, compressor, this);
|
||||
descend();
|
||||
handle_defined_after_hoist(this);
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Try, function(tw, descend, compressor) {
|
||||
reset_block_variables(compressor, this);
|
||||
push(tw);
|
||||
this.body.walk(tw);
|
||||
pop(tw);
|
||||
if (this.bcatch) {
|
||||
push(tw);
|
||||
this.bcatch.walk(tw);
|
||||
pop(tw);
|
||||
}
|
||||
if (this.bfinally) this.bfinally.walk(tw);
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_Unary, function(tw) {
|
||||
var node = this;
|
||||
if (node.operator !== "++" && node.operator !== "--") return;
|
||||
var exp = node.expression;
|
||||
if (!(exp instanceof AST_SymbolRef)) return;
|
||||
var def = exp.definition();
|
||||
var safe = safe_to_assign(tw, def, exp.scope, true);
|
||||
def.assignments++;
|
||||
if (!safe) return;
|
||||
var fixed = def.fixed;
|
||||
if (!fixed) return;
|
||||
def.references.push(exp);
|
||||
def.chained = true;
|
||||
def.fixed = function() {
|
||||
return make_node(AST_Binary, node, {
|
||||
operator: node.operator.slice(0, -1),
|
||||
left: make_node(AST_UnaryPrefix, node, {
|
||||
operator: "+",
|
||||
expression: fixed instanceof AST_Node ? fixed : fixed()
|
||||
}),
|
||||
right: make_node(AST_Number, node, {
|
||||
value: 1
|
||||
})
|
||||
});
|
||||
};
|
||||
mark(tw, def, true);
|
||||
return true;
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_VarDef, function(tw, descend) {
|
||||
var node = this;
|
||||
if (node.name instanceof AST_Destructuring) {
|
||||
suppress(node.name);
|
||||
return;
|
||||
}
|
||||
var d = node.name.definition();
|
||||
if (node.value) {
|
||||
if (safe_to_assign(tw, d, node.name.scope, node.value)) {
|
||||
d.fixed = function() {
|
||||
return node.value;
|
||||
};
|
||||
tw.loop_ids.set(d.id, tw.in_loop);
|
||||
mark(tw, d, false);
|
||||
descend();
|
||||
mark(tw, d, true);
|
||||
return true;
|
||||
} else {
|
||||
d.fixed = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
def_reduce_vars(AST_While, function(tw, descend, compressor) {
|
||||
reset_block_variables(compressor, this);
|
||||
const saved_loop = tw.in_loop;
|
||||
tw.in_loop = this;
|
||||
push(tw);
|
||||
descend();
|
||||
pop(tw);
|
||||
tw.in_loop = saved_loop;
|
||||
return true;
|
||||
});
|
1505
my-app/node_modules/terser/lib/compress/tighten-body.js
generated
vendored
Executable file
1505
my-app/node_modules/terser/lib/compress/tighten-body.js
generated
vendored
Executable file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue