summaryrefslogtreecommitdiff
path: root/node_modules/terser/lib/compress/evaluate.js
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2023-11-05 17:16:09 +0800
committerlittleblack111 <littleblack11111@gmail.com>2023-11-05 17:16:09 +0800
commitd1d9f00b1bd660842addf5e6b1d1b64c852a9eea (patch)
tree60fb37ae73a1109d95301de5ec9b61b5726a4b61 /node_modules/terser/lib/compress/evaluate.js
parent7c85166c4cbdd862fc4252007d2df5befcbc2a58 (diff)
added hover prefixer
Diffstat (limited to 'node_modules/terser/lib/compress/evaluate.js')
-rw-r--r--node_modules/terser/lib/compress/evaluate.js488
1 files changed, 488 insertions, 0 deletions
diff --git a/node_modules/terser/lib/compress/evaluate.js b/node_modules/terser/lib/compress/evaluate.js
new file mode 100644
index 00000000..d249ee35
--- /dev/null
+++ b/node_modules/terser/lib/compress/evaluate.js
@@ -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);