Buckets:
arudradey/ml-cpu-storage / emsdk /upstream /emscripten /node_modules /terser /lib /compress /evaluate.js
| /*********************************************************************** | |
| 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 | |
| && unaryPrefix.has(this.operator) | |
| && ( | |
| // `this.expression` may be an `AST_RegExp`, | |
| // so not only `.is_constant()`. | |
| this.expression instanceof AST_Constant | |
| || this.expression.is_constant() | |
| ); | |
| } | |
| }); | |
| 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(); | |
| }); | |
| const supports_bigint = typeof BigInt === "function"; | |
| def_eval(AST_BigInt, function () { | |
| if (supports_bigint) { | |
| return BigInt(this.value); | |
| } else { | |
| 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; | |
| if (compressor.option("typeofs") | |
| && this.operator == "typeof") { | |
| // Function would be evaluated to an array and so typeof would | |
| // incorrectly return 'object'. Hence making is a special case. | |
| if (e instanceof AST_Lambda | |
| || e instanceof AST_SymbolRef | |
| && e.fixed_value() instanceof AST_Lambda) { | |
| return typeof function () { }; | |
| } | |
| if ( | |
| (e instanceof AST_Object | |
| || e instanceof AST_Array | |
| || (e instanceof AST_SymbolRef | |
| && (e.fixed_value() instanceof AST_Object | |
| || e.fixed_value() instanceof AST_Array))) | |
| && !e.has_side_effects(compressor) | |
| ) { | |
| return typeof {}; | |
| } | |
| } | |
| 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; | |
| 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; | |
| } | |
| // Do not mix BigInt and Number; Don't use `>>>` on BigInt or `/ 0n` | |
| if ( | |
| (typeof left === "bigint") !== (typeof right === "bigint") | |
| || typeof left === "bigint" | |
| && (this.operator === ">>>" | |
| || this.operator === "/" && Number(right) === 0) | |
| ) { | |
| return this; | |
| } | |
| var result; | |
| 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 = 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 (typeof result === "number" && 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); | |
Xet Storage Details
- Size:
- 17 kB
- Xet hash:
- 6cfb81c2a3f93f865a19f0e20e355655067a68240271e08a23a3d3a1fda866f2
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.