diff options
Diffstat (limited to 'node_modules/terser/lib/compress/inference.js')
| -rw-r--r-- | node_modules/terser/lib/compress/inference.js | 984 |
1 files changed, 0 insertions, 984 deletions
diff --git a/node_modules/terser/lib/compress/inference.js b/node_modules/terser/lib/compress/inference.js deleted file mode 100644 index b9f48813..00000000 --- a/node_modules/terser/lib/compress/inference.js +++ /dev/null @@ -1,984 +0,0 @@ -/*********************************************************************** - - 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 bitwise_binop = makePredicate("<<< >> << & | ^ ~"); -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 is a 32 bit integer (IE results from bitwise ops, or is an integer constant fitting in that size -(function(def_is_32_bit_integer) { - def_is_32_bit_integer(AST_Node, return_false); - def_is_32_bit_integer(AST_Number, function() { - return this.value === (this.value | 0); - }); - def_is_32_bit_integer(AST_UnaryPrefix, function() { - return this.operator == "~" ? this.expression.is_number() - : this.operator === "+" ? this.expression.is_32_bit_integer() - : false; - }); - def_is_32_bit_integer(AST_Binary, function() { - return bitwise_binop.has(this.operator); - }); -}(function (node, func) { - node.DEFMETHOD("is_32_bit_integer", 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 != null; - }); - 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); - }); -}); - -(function (def_bitwise_negate) { - function basic_negation(exp) { - return make_node(AST_UnaryPrefix, exp, { - operator: "~", - expression: exp - }); - } - - def_bitwise_negate(AST_Node, function() { - return basic_negation(this); - }); - - def_bitwise_negate(AST_Number, function() { - const neg = ~this.value; - if (neg.toString().length > this.value.toString().length) { - return basic_negation(this); - } - return make_node(AST_Number, this, { value: neg }); - }); - - def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) { - if (this.operator == "~" && (in_32_bit_context || this.expression.is_32_bit_integer())) { - return this.expression; - } else { - return basic_negation(this); - } - }); -})(function (node, func) { - node.DEFMETHOD("bitwise_negate", func); -}); - -// 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); - } -} |
