[79a0317] | 1 | /***********************************************************************
|
---|
| 2 |
|
---|
| 3 | A JavaScript tokenizer / parser / beautifier / compressor.
|
---|
| 4 | https://github.com/mishoo/UglifyJS2
|
---|
| 5 |
|
---|
| 6 | -------------------------------- (C) ---------------------------------
|
---|
| 7 |
|
---|
| 8 | Author: Mihai Bazon
|
---|
| 9 | <mihai.bazon@gmail.com>
|
---|
| 10 | http://mihai.bazon.net/blog
|
---|
| 11 |
|
---|
| 12 | Distributed under the BSD license:
|
---|
| 13 |
|
---|
| 14 | Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
---|
| 15 |
|
---|
| 16 | Redistribution and use in source and binary forms, with or without
|
---|
| 17 | modification, are permitted provided that the following conditions
|
---|
| 18 | are met:
|
---|
| 19 |
|
---|
| 20 | * Redistributions of source code must retain the above
|
---|
| 21 | copyright notice, this list of conditions and the following
|
---|
| 22 | disclaimer.
|
---|
| 23 |
|
---|
| 24 | * Redistributions in binary form must reproduce the above
|
---|
| 25 | copyright notice, this list of conditions and the following
|
---|
| 26 | disclaimer in the documentation and/or other materials
|
---|
| 27 | provided with the distribution.
|
---|
| 28 |
|
---|
| 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
---|
| 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
---|
| 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
---|
| 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
---|
| 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
---|
| 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
---|
| 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
---|
| 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
| 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
---|
| 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
---|
| 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
---|
| 40 | SUCH DAMAGE.
|
---|
| 41 |
|
---|
| 42 | ***********************************************************************/
|
---|
| 43 |
|
---|
| 44 | import {
|
---|
| 45 | AST_Array,
|
---|
| 46 | AST_Arrow,
|
---|
| 47 | AST_Assign,
|
---|
| 48 | AST_BigInt,
|
---|
| 49 | AST_Binary,
|
---|
| 50 | AST_Block,
|
---|
| 51 | AST_BlockStatement,
|
---|
| 52 | AST_Call,
|
---|
| 53 | AST_Case,
|
---|
| 54 | AST_Chain,
|
---|
| 55 | AST_Class,
|
---|
| 56 | AST_DefClass,
|
---|
| 57 | AST_ClassStaticBlock,
|
---|
| 58 | AST_ClassProperty,
|
---|
| 59 | AST_ConciseMethod,
|
---|
| 60 | AST_Conditional,
|
---|
| 61 | AST_Constant,
|
---|
| 62 | AST_Definitions,
|
---|
| 63 | AST_Dot,
|
---|
| 64 | AST_EmptyStatement,
|
---|
| 65 | AST_Expansion,
|
---|
| 66 | AST_False,
|
---|
| 67 | AST_ForIn,
|
---|
| 68 | AST_Function,
|
---|
| 69 | AST_If,
|
---|
| 70 | AST_Import,
|
---|
| 71 | AST_ImportMeta,
|
---|
| 72 | AST_Jump,
|
---|
| 73 | AST_LabeledStatement,
|
---|
| 74 | AST_Lambda,
|
---|
| 75 | AST_New,
|
---|
| 76 | AST_Node,
|
---|
| 77 | AST_Null,
|
---|
| 78 | AST_Number,
|
---|
| 79 | AST_Object,
|
---|
| 80 | AST_ObjectGetter,
|
---|
| 81 | AST_ObjectKeyVal,
|
---|
| 82 | AST_ObjectProperty,
|
---|
| 83 | AST_ObjectSetter,
|
---|
| 84 | AST_PropAccess,
|
---|
| 85 | AST_RegExp,
|
---|
| 86 | AST_Return,
|
---|
| 87 | AST_Scope,
|
---|
| 88 | AST_Sequence,
|
---|
| 89 | AST_SimpleStatement,
|
---|
| 90 | AST_Statement,
|
---|
| 91 | AST_String,
|
---|
| 92 | AST_Sub,
|
---|
| 93 | AST_Switch,
|
---|
| 94 | AST_SwitchBranch,
|
---|
| 95 | AST_SymbolClassProperty,
|
---|
| 96 | AST_SymbolDeclaration,
|
---|
| 97 | AST_SymbolRef,
|
---|
| 98 | AST_TemplateSegment,
|
---|
| 99 | AST_TemplateString,
|
---|
| 100 | AST_This,
|
---|
| 101 | AST_True,
|
---|
| 102 | AST_Try,
|
---|
| 103 | AST_Unary,
|
---|
| 104 | AST_UnaryPostfix,
|
---|
| 105 | AST_UnaryPrefix,
|
---|
| 106 | AST_Undefined,
|
---|
| 107 | AST_VarDef,
|
---|
| 108 |
|
---|
| 109 | walk,
|
---|
| 110 | walk_abort,
|
---|
| 111 |
|
---|
| 112 | _PURE
|
---|
| 113 | } from "../ast.js";
|
---|
| 114 | import {
|
---|
| 115 | makePredicate,
|
---|
| 116 | return_true,
|
---|
| 117 | return_false,
|
---|
| 118 | return_null,
|
---|
| 119 | return_this,
|
---|
| 120 | make_node,
|
---|
| 121 | member,
|
---|
| 122 | has_annotation,
|
---|
| 123 | } from "../utils/index.js";
|
---|
| 124 | import { make_sequence, best_of_expression, read_property, requires_sequence_to_maintain_binding } from "./common.js";
|
---|
| 125 |
|
---|
| 126 | import { INLINED, UNDEFINED, has_flag } from "./compressor-flags.js";
|
---|
| 127 | import { pure_prop_access_globals, is_pure_native_fn, is_pure_native_method } from "./native-objects.js";
|
---|
| 128 |
|
---|
| 129 | // Functions and methods to infer certain facts about expressions
|
---|
| 130 | // It's not always possible to be 100% sure about something just by static analysis,
|
---|
| 131 | // so `true` means yes, and `false` means maybe
|
---|
| 132 |
|
---|
| 133 | export const is_undeclared_ref = (node) =>
|
---|
| 134 | node instanceof AST_SymbolRef && node.definition().undeclared;
|
---|
| 135 |
|
---|
| 136 | export const bitwise_binop = makePredicate("<<< >> << & | ^ ~");
|
---|
| 137 | export const lazy_op = makePredicate("&& || ??");
|
---|
| 138 | export const unary_side_effects = makePredicate("delete ++ --");
|
---|
| 139 |
|
---|
| 140 | // methods to determine whether an expression has a boolean result type
|
---|
| 141 | (function(def_is_boolean) {
|
---|
| 142 | const unary_bool = makePredicate("! delete");
|
---|
| 143 | const binary_bool = makePredicate("in instanceof == != === !== < <= >= >");
|
---|
| 144 | def_is_boolean(AST_Node, return_false);
|
---|
| 145 | def_is_boolean(AST_UnaryPrefix, function() {
|
---|
| 146 | return unary_bool.has(this.operator);
|
---|
| 147 | });
|
---|
| 148 | def_is_boolean(AST_Binary, function() {
|
---|
| 149 | return binary_bool.has(this.operator)
|
---|
| 150 | || lazy_op.has(this.operator)
|
---|
| 151 | && this.left.is_boolean()
|
---|
| 152 | && this.right.is_boolean();
|
---|
| 153 | });
|
---|
| 154 | def_is_boolean(AST_Conditional, function() {
|
---|
| 155 | return this.consequent.is_boolean() && this.alternative.is_boolean();
|
---|
| 156 | });
|
---|
| 157 | def_is_boolean(AST_Assign, function() {
|
---|
| 158 | return this.operator == "=" && this.right.is_boolean();
|
---|
| 159 | });
|
---|
| 160 | def_is_boolean(AST_Sequence, function() {
|
---|
| 161 | return this.tail_node().is_boolean();
|
---|
| 162 | });
|
---|
| 163 | def_is_boolean(AST_True, return_true);
|
---|
| 164 | def_is_boolean(AST_False, return_true);
|
---|
| 165 | })(function(node, func) {
|
---|
| 166 | node.DEFMETHOD("is_boolean", func);
|
---|
| 167 | });
|
---|
| 168 |
|
---|
| 169 | // methods to determine if an expression has a numeric result type
|
---|
| 170 | (function(def_is_number) {
|
---|
| 171 | def_is_number(AST_Node, return_false);
|
---|
| 172 | def_is_number(AST_Number, return_true);
|
---|
| 173 | const unary = makePredicate("+ - ~ ++ --");
|
---|
| 174 | def_is_number(AST_Unary, function() {
|
---|
| 175 | return unary.has(this.operator) && !(this.expression instanceof AST_BigInt);
|
---|
| 176 | });
|
---|
| 177 | const numeric_ops = makePredicate("- * / % & | ^ << >> >>>");
|
---|
| 178 | def_is_number(AST_Binary, function(compressor) {
|
---|
| 179 | return numeric_ops.has(this.operator) || this.operator == "+"
|
---|
| 180 | && this.left.is_number(compressor)
|
---|
| 181 | && this.right.is_number(compressor);
|
---|
| 182 | });
|
---|
| 183 | def_is_number(AST_Assign, function(compressor) {
|
---|
| 184 | return numeric_ops.has(this.operator.slice(0, -1))
|
---|
| 185 | || this.operator == "=" && this.right.is_number(compressor);
|
---|
| 186 | });
|
---|
| 187 | def_is_number(AST_Sequence, function(compressor) {
|
---|
| 188 | return this.tail_node().is_number(compressor);
|
---|
| 189 | });
|
---|
| 190 | def_is_number(AST_Conditional, function(compressor) {
|
---|
| 191 | return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
|
---|
| 192 | });
|
---|
| 193 | })(function(node, func) {
|
---|
| 194 | node.DEFMETHOD("is_number", func);
|
---|
| 195 | });
|
---|
| 196 |
|
---|
| 197 | // 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
|
---|
| 198 | (function(def_is_32_bit_integer) {
|
---|
| 199 | def_is_32_bit_integer(AST_Node, return_false);
|
---|
| 200 | def_is_32_bit_integer(AST_Number, function() {
|
---|
| 201 | return this.value === (this.value | 0);
|
---|
| 202 | });
|
---|
| 203 | def_is_32_bit_integer(AST_UnaryPrefix, function() {
|
---|
| 204 | return this.operator == "~" ? this.expression.is_number()
|
---|
| 205 | : this.operator === "+" ? this.expression.is_32_bit_integer()
|
---|
| 206 | : false;
|
---|
| 207 | });
|
---|
| 208 | def_is_32_bit_integer(AST_Binary, function() {
|
---|
| 209 | return bitwise_binop.has(this.operator);
|
---|
| 210 | });
|
---|
| 211 | }(function (node, func) {
|
---|
| 212 | node.DEFMETHOD("is_32_bit_integer", func);
|
---|
| 213 | }));
|
---|
| 214 |
|
---|
| 215 | // methods to determine if an expression has a string result type
|
---|
| 216 | (function(def_is_string) {
|
---|
| 217 | def_is_string(AST_Node, return_false);
|
---|
| 218 | def_is_string(AST_String, return_true);
|
---|
| 219 | def_is_string(AST_TemplateString, return_true);
|
---|
| 220 | def_is_string(AST_UnaryPrefix, function() {
|
---|
| 221 | return this.operator == "typeof";
|
---|
| 222 | });
|
---|
| 223 | def_is_string(AST_Binary, function(compressor) {
|
---|
| 224 | return this.operator == "+" &&
|
---|
| 225 | (this.left.is_string(compressor) || this.right.is_string(compressor));
|
---|
| 226 | });
|
---|
| 227 | def_is_string(AST_Assign, function(compressor) {
|
---|
| 228 | return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
|
---|
| 229 | });
|
---|
| 230 | def_is_string(AST_Sequence, function(compressor) {
|
---|
| 231 | return this.tail_node().is_string(compressor);
|
---|
| 232 | });
|
---|
| 233 | def_is_string(AST_Conditional, function(compressor) {
|
---|
| 234 | return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
|
---|
| 235 | });
|
---|
| 236 | })(function(node, func) {
|
---|
| 237 | node.DEFMETHOD("is_string", func);
|
---|
| 238 | });
|
---|
| 239 |
|
---|
| 240 | export function is_undefined(node, compressor) {
|
---|
| 241 | return (
|
---|
| 242 | has_flag(node, UNDEFINED)
|
---|
| 243 | || node instanceof AST_Undefined
|
---|
| 244 | || node instanceof AST_UnaryPrefix
|
---|
| 245 | && node.operator == "void"
|
---|
| 246 | && !node.expression.has_side_effects(compressor)
|
---|
| 247 | );
|
---|
| 248 | }
|
---|
| 249 |
|
---|
| 250 | // Is the node explicitly null or undefined.
|
---|
| 251 | function is_null_or_undefined(node, compressor) {
|
---|
| 252 | let fixed;
|
---|
| 253 | return (
|
---|
| 254 | node instanceof AST_Null
|
---|
| 255 | || is_undefined(node, compressor)
|
---|
| 256 | || (
|
---|
| 257 | node instanceof AST_SymbolRef
|
---|
| 258 | && (fixed = node.definition().fixed) instanceof AST_Node
|
---|
| 259 | && is_nullish(fixed, compressor)
|
---|
| 260 | )
|
---|
| 261 | );
|
---|
| 262 | }
|
---|
| 263 |
|
---|
| 264 | // Find out if this expression is optionally chained from a base-point that we
|
---|
| 265 | // can statically analyze as null or undefined.
|
---|
| 266 | export function is_nullish_shortcircuited(node, compressor) {
|
---|
| 267 | if (node instanceof AST_PropAccess || node instanceof AST_Call) {
|
---|
| 268 | return (
|
---|
| 269 | (node.optional && is_null_or_undefined(node.expression, compressor))
|
---|
| 270 | || is_nullish_shortcircuited(node.expression, compressor)
|
---|
| 271 | );
|
---|
| 272 | }
|
---|
| 273 | if (node instanceof AST_Chain) return is_nullish_shortcircuited(node.expression, compressor);
|
---|
| 274 | return false;
|
---|
| 275 | }
|
---|
| 276 |
|
---|
| 277 | // Find out if something is == null, or can short circuit into nullish.
|
---|
| 278 | // Used to optimize ?. and ??
|
---|
| 279 | export function is_nullish(node, compressor) {
|
---|
| 280 | if (is_null_or_undefined(node, compressor)) return true;
|
---|
| 281 | return is_nullish_shortcircuited(node, compressor);
|
---|
| 282 | }
|
---|
| 283 |
|
---|
| 284 | // Determine if expression might cause side effects
|
---|
| 285 | // If there's a possibility that a node may change something when it's executed, this returns true
|
---|
| 286 | (function(def_has_side_effects) {
|
---|
| 287 | def_has_side_effects(AST_Node, return_true);
|
---|
| 288 |
|
---|
| 289 | def_has_side_effects(AST_EmptyStatement, return_false);
|
---|
| 290 | def_has_side_effects(AST_Constant, return_false);
|
---|
| 291 | def_has_side_effects(AST_This, return_false);
|
---|
| 292 |
|
---|
| 293 | function any(list, compressor) {
|
---|
| 294 | for (var i = list.length; --i >= 0;)
|
---|
| 295 | if (list[i].has_side_effects(compressor))
|
---|
| 296 | return true;
|
---|
| 297 | return false;
|
---|
| 298 | }
|
---|
| 299 |
|
---|
| 300 | def_has_side_effects(AST_Block, function(compressor) {
|
---|
| 301 | return any(this.body, compressor);
|
---|
| 302 | });
|
---|
| 303 | def_has_side_effects(AST_Call, function(compressor) {
|
---|
| 304 | if (
|
---|
| 305 | !this.is_callee_pure(compressor)
|
---|
| 306 | && (!this.expression.is_call_pure(compressor)
|
---|
| 307 | || this.expression.has_side_effects(compressor))
|
---|
| 308 | ) {
|
---|
| 309 | return true;
|
---|
| 310 | }
|
---|
| 311 | return any(this.args, compressor);
|
---|
| 312 | });
|
---|
| 313 | def_has_side_effects(AST_Switch, function(compressor) {
|
---|
| 314 | return this.expression.has_side_effects(compressor)
|
---|
| 315 | || any(this.body, compressor);
|
---|
| 316 | });
|
---|
| 317 | def_has_side_effects(AST_Case, function(compressor) {
|
---|
| 318 | return this.expression.has_side_effects(compressor)
|
---|
| 319 | || any(this.body, compressor);
|
---|
| 320 | });
|
---|
| 321 | def_has_side_effects(AST_Try, function(compressor) {
|
---|
| 322 | return this.body.has_side_effects(compressor)
|
---|
| 323 | || this.bcatch && this.bcatch.has_side_effects(compressor)
|
---|
| 324 | || this.bfinally && this.bfinally.has_side_effects(compressor);
|
---|
| 325 | });
|
---|
| 326 | def_has_side_effects(AST_If, function(compressor) {
|
---|
| 327 | return this.condition.has_side_effects(compressor)
|
---|
| 328 | || this.body && this.body.has_side_effects(compressor)
|
---|
| 329 | || this.alternative && this.alternative.has_side_effects(compressor);
|
---|
| 330 | });
|
---|
| 331 | def_has_side_effects(AST_ImportMeta, return_false);
|
---|
| 332 | def_has_side_effects(AST_LabeledStatement, function(compressor) {
|
---|
| 333 | return this.body.has_side_effects(compressor);
|
---|
| 334 | });
|
---|
| 335 | def_has_side_effects(AST_SimpleStatement, function(compressor) {
|
---|
| 336 | return this.body.has_side_effects(compressor);
|
---|
| 337 | });
|
---|
| 338 | def_has_side_effects(AST_Lambda, return_false);
|
---|
| 339 | def_has_side_effects(AST_Class, function (compressor) {
|
---|
| 340 | if (this.extends && this.extends.has_side_effects(compressor)) {
|
---|
| 341 | return true;
|
---|
| 342 | }
|
---|
| 343 | return any(this.properties, compressor);
|
---|
| 344 | });
|
---|
| 345 | def_has_side_effects(AST_ClassStaticBlock, function(compressor) {
|
---|
| 346 | return any(this.body, compressor);
|
---|
| 347 | });
|
---|
| 348 | def_has_side_effects(AST_Binary, function(compressor) {
|
---|
| 349 | return this.left.has_side_effects(compressor)
|
---|
| 350 | || this.right.has_side_effects(compressor);
|
---|
| 351 | });
|
---|
| 352 | def_has_side_effects(AST_Assign, return_true);
|
---|
| 353 | def_has_side_effects(AST_Conditional, function(compressor) {
|
---|
| 354 | return this.condition.has_side_effects(compressor)
|
---|
| 355 | || this.consequent.has_side_effects(compressor)
|
---|
| 356 | || this.alternative.has_side_effects(compressor);
|
---|
| 357 | });
|
---|
| 358 | def_has_side_effects(AST_Unary, function(compressor) {
|
---|
| 359 | return unary_side_effects.has(this.operator)
|
---|
| 360 | || this.expression.has_side_effects(compressor);
|
---|
| 361 | });
|
---|
| 362 | def_has_side_effects(AST_SymbolRef, function(compressor) {
|
---|
| 363 | return !this.is_declared(compressor) && !pure_prop_access_globals.has(this.name);
|
---|
| 364 | });
|
---|
| 365 | def_has_side_effects(AST_SymbolClassProperty, return_false);
|
---|
| 366 | def_has_side_effects(AST_SymbolDeclaration, return_false);
|
---|
| 367 | def_has_side_effects(AST_Object, function(compressor) {
|
---|
| 368 | return any(this.properties, compressor);
|
---|
| 369 | });
|
---|
| 370 | def_has_side_effects(AST_ObjectProperty, function(compressor) {
|
---|
| 371 | return (
|
---|
| 372 | this.computed_key() && this.key.has_side_effects(compressor)
|
---|
| 373 | || this.value && this.value.has_side_effects(compressor)
|
---|
| 374 | );
|
---|
| 375 | });
|
---|
| 376 | def_has_side_effects(AST_ClassProperty, function(compressor) {
|
---|
| 377 | return (
|
---|
| 378 | this.computed_key() && this.key.has_side_effects(compressor)
|
---|
| 379 | || this.static && this.value && this.value.has_side_effects(compressor)
|
---|
| 380 | );
|
---|
| 381 | });
|
---|
| 382 | def_has_side_effects(AST_ConciseMethod, function(compressor) {
|
---|
| 383 | return this.computed_key() && this.key.has_side_effects(compressor);
|
---|
| 384 | });
|
---|
| 385 | def_has_side_effects(AST_ObjectGetter, function(compressor) {
|
---|
| 386 | return this.computed_key() && this.key.has_side_effects(compressor);
|
---|
| 387 | });
|
---|
| 388 | def_has_side_effects(AST_ObjectSetter, function(compressor) {
|
---|
| 389 | return this.computed_key() && this.key.has_side_effects(compressor);
|
---|
| 390 | });
|
---|
| 391 | def_has_side_effects(AST_Array, function(compressor) {
|
---|
| 392 | return any(this.elements, compressor);
|
---|
| 393 | });
|
---|
| 394 | def_has_side_effects(AST_Dot, function(compressor) {
|
---|
| 395 | if (is_nullish(this, compressor)) {
|
---|
| 396 | return this.expression.has_side_effects(compressor);
|
---|
| 397 | }
|
---|
| 398 | if (!this.optional && this.expression.may_throw_on_access(compressor)) {
|
---|
| 399 | return true;
|
---|
| 400 | }
|
---|
| 401 |
|
---|
| 402 | return this.expression.has_side_effects(compressor);
|
---|
| 403 | });
|
---|
| 404 | def_has_side_effects(AST_Sub, function(compressor) {
|
---|
| 405 | if (is_nullish(this, compressor)) {
|
---|
| 406 | return this.expression.has_side_effects(compressor);
|
---|
| 407 | }
|
---|
| 408 | if (!this.optional && this.expression.may_throw_on_access(compressor)) {
|
---|
| 409 | return true;
|
---|
| 410 | }
|
---|
| 411 |
|
---|
| 412 | var property = this.property.has_side_effects(compressor);
|
---|
| 413 | if (property && this.optional) return true; // "?." is a condition
|
---|
| 414 |
|
---|
| 415 | return property || this.expression.has_side_effects(compressor);
|
---|
| 416 | });
|
---|
| 417 | def_has_side_effects(AST_Chain, function (compressor) {
|
---|
| 418 | return this.expression.has_side_effects(compressor);
|
---|
| 419 | });
|
---|
| 420 | def_has_side_effects(AST_Sequence, function(compressor) {
|
---|
| 421 | return any(this.expressions, compressor);
|
---|
| 422 | });
|
---|
| 423 | def_has_side_effects(AST_Definitions, function(compressor) {
|
---|
| 424 | return any(this.definitions, compressor);
|
---|
| 425 | });
|
---|
| 426 | def_has_side_effects(AST_VarDef, function() {
|
---|
| 427 | return this.value != null;
|
---|
| 428 | });
|
---|
| 429 | def_has_side_effects(AST_TemplateSegment, return_false);
|
---|
| 430 | def_has_side_effects(AST_TemplateString, function(compressor) {
|
---|
| 431 | return any(this.segments, compressor);
|
---|
| 432 | });
|
---|
| 433 | })(function(node, func) {
|
---|
| 434 | node.DEFMETHOD("has_side_effects", func);
|
---|
| 435 | });
|
---|
| 436 |
|
---|
| 437 | // determine if expression may throw
|
---|
| 438 | (function(def_may_throw) {
|
---|
| 439 | def_may_throw(AST_Node, return_true);
|
---|
| 440 |
|
---|
| 441 | def_may_throw(AST_Constant, return_false);
|
---|
| 442 | def_may_throw(AST_EmptyStatement, return_false);
|
---|
| 443 | def_may_throw(AST_Lambda, return_false);
|
---|
| 444 | def_may_throw(AST_SymbolDeclaration, return_false);
|
---|
| 445 | def_may_throw(AST_This, return_false);
|
---|
| 446 | def_may_throw(AST_ImportMeta, return_false);
|
---|
| 447 |
|
---|
| 448 | function any(list, compressor) {
|
---|
| 449 | for (var i = list.length; --i >= 0;)
|
---|
| 450 | if (list[i].may_throw(compressor))
|
---|
| 451 | return true;
|
---|
| 452 | return false;
|
---|
| 453 | }
|
---|
| 454 |
|
---|
| 455 | def_may_throw(AST_Class, function(compressor) {
|
---|
| 456 | if (this.extends && this.extends.may_throw(compressor)) return true;
|
---|
| 457 | return any(this.properties, compressor);
|
---|
| 458 | });
|
---|
| 459 | def_may_throw(AST_ClassStaticBlock, function (compressor) {
|
---|
| 460 | return any(this.body, compressor);
|
---|
| 461 | });
|
---|
| 462 |
|
---|
| 463 | def_may_throw(AST_Array, function(compressor) {
|
---|
| 464 | return any(this.elements, compressor);
|
---|
| 465 | });
|
---|
| 466 | def_may_throw(AST_Assign, function(compressor) {
|
---|
| 467 | if (this.right.may_throw(compressor)) return true;
|
---|
| 468 | if (!compressor.has_directive("use strict")
|
---|
| 469 | && this.operator == "="
|
---|
| 470 | && this.left instanceof AST_SymbolRef) {
|
---|
| 471 | return false;
|
---|
| 472 | }
|
---|
| 473 | return this.left.may_throw(compressor);
|
---|
| 474 | });
|
---|
| 475 | def_may_throw(AST_Binary, function(compressor) {
|
---|
| 476 | return this.left.may_throw(compressor)
|
---|
| 477 | || this.right.may_throw(compressor);
|
---|
| 478 | });
|
---|
| 479 | def_may_throw(AST_Block, function(compressor) {
|
---|
| 480 | return any(this.body, compressor);
|
---|
| 481 | });
|
---|
| 482 | def_may_throw(AST_Call, function(compressor) {
|
---|
| 483 | if (is_nullish(this, compressor)) return false;
|
---|
| 484 | if (any(this.args, compressor)) return true;
|
---|
| 485 | if (this.is_callee_pure(compressor)) return false;
|
---|
| 486 | if (this.expression.may_throw(compressor)) return true;
|
---|
| 487 | return !(this.expression instanceof AST_Lambda)
|
---|
| 488 | || any(this.expression.body, compressor);
|
---|
| 489 | });
|
---|
| 490 | def_may_throw(AST_Case, function(compressor) {
|
---|
| 491 | return this.expression.may_throw(compressor)
|
---|
| 492 | || any(this.body, compressor);
|
---|
| 493 | });
|
---|
| 494 | def_may_throw(AST_Conditional, function(compressor) {
|
---|
| 495 | return this.condition.may_throw(compressor)
|
---|
| 496 | || this.consequent.may_throw(compressor)
|
---|
| 497 | || this.alternative.may_throw(compressor);
|
---|
| 498 | });
|
---|
| 499 | def_may_throw(AST_Definitions, function(compressor) {
|
---|
| 500 | return any(this.definitions, compressor);
|
---|
| 501 | });
|
---|
| 502 | def_may_throw(AST_If, function(compressor) {
|
---|
| 503 | return this.condition.may_throw(compressor)
|
---|
| 504 | || this.body && this.body.may_throw(compressor)
|
---|
| 505 | || this.alternative && this.alternative.may_throw(compressor);
|
---|
| 506 | });
|
---|
| 507 | def_may_throw(AST_LabeledStatement, function(compressor) {
|
---|
| 508 | return this.body.may_throw(compressor);
|
---|
| 509 | });
|
---|
| 510 | def_may_throw(AST_Object, function(compressor) {
|
---|
| 511 | return any(this.properties, compressor);
|
---|
| 512 | });
|
---|
| 513 | def_may_throw(AST_ObjectProperty, function(compressor) {
|
---|
| 514 | // TODO key may throw too
|
---|
| 515 | return this.value ? this.value.may_throw(compressor) : false;
|
---|
| 516 | });
|
---|
| 517 | def_may_throw(AST_ClassProperty, function(compressor) {
|
---|
| 518 | return (
|
---|
| 519 | this.computed_key() && this.key.may_throw(compressor)
|
---|
| 520 | || this.static && this.value && this.value.may_throw(compressor)
|
---|
| 521 | );
|
---|
| 522 | });
|
---|
| 523 | def_may_throw(AST_ConciseMethod, function(compressor) {
|
---|
| 524 | return this.computed_key() && this.key.may_throw(compressor);
|
---|
| 525 | });
|
---|
| 526 | def_may_throw(AST_ObjectGetter, function(compressor) {
|
---|
| 527 | return this.computed_key() && this.key.may_throw(compressor);
|
---|
| 528 | });
|
---|
| 529 | def_may_throw(AST_ObjectSetter, function(compressor) {
|
---|
| 530 | return this.computed_key() && this.key.may_throw(compressor);
|
---|
| 531 | });
|
---|
| 532 | def_may_throw(AST_Return, function(compressor) {
|
---|
| 533 | return this.value && this.value.may_throw(compressor);
|
---|
| 534 | });
|
---|
| 535 | def_may_throw(AST_Sequence, function(compressor) {
|
---|
| 536 | return any(this.expressions, compressor);
|
---|
| 537 | });
|
---|
| 538 | def_may_throw(AST_SimpleStatement, function(compressor) {
|
---|
| 539 | return this.body.may_throw(compressor);
|
---|
| 540 | });
|
---|
| 541 | def_may_throw(AST_Dot, function(compressor) {
|
---|
| 542 | if (is_nullish(this, compressor)) return false;
|
---|
| 543 | return !this.optional && this.expression.may_throw_on_access(compressor)
|
---|
| 544 | || this.expression.may_throw(compressor);
|
---|
| 545 | });
|
---|
| 546 | def_may_throw(AST_Sub, function(compressor) {
|
---|
| 547 | if (is_nullish(this, compressor)) return false;
|
---|
| 548 | return !this.optional && this.expression.may_throw_on_access(compressor)
|
---|
| 549 | || this.expression.may_throw(compressor)
|
---|
| 550 | || this.property.may_throw(compressor);
|
---|
| 551 | });
|
---|
| 552 | def_may_throw(AST_Chain, function(compressor) {
|
---|
| 553 | return this.expression.may_throw(compressor);
|
---|
| 554 | });
|
---|
| 555 | def_may_throw(AST_Switch, function(compressor) {
|
---|
| 556 | return this.expression.may_throw(compressor)
|
---|
| 557 | || any(this.body, compressor);
|
---|
| 558 | });
|
---|
| 559 | def_may_throw(AST_SymbolRef, function(compressor) {
|
---|
| 560 | return !this.is_declared(compressor) && !pure_prop_access_globals.has(this.name);
|
---|
| 561 | });
|
---|
| 562 | def_may_throw(AST_SymbolClassProperty, return_false);
|
---|
| 563 | def_may_throw(AST_Try, function(compressor) {
|
---|
| 564 | return this.bcatch ? this.bcatch.may_throw(compressor) : this.body.may_throw(compressor)
|
---|
| 565 | || this.bfinally && this.bfinally.may_throw(compressor);
|
---|
| 566 | });
|
---|
| 567 | def_may_throw(AST_Unary, function(compressor) {
|
---|
| 568 | if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef)
|
---|
| 569 | return false;
|
---|
| 570 | return this.expression.may_throw(compressor);
|
---|
| 571 | });
|
---|
| 572 | def_may_throw(AST_VarDef, function(compressor) {
|
---|
| 573 | if (!this.value) return false;
|
---|
| 574 | return this.value.may_throw(compressor);
|
---|
| 575 | });
|
---|
| 576 | })(function(node, func) {
|
---|
| 577 | node.DEFMETHOD("may_throw", func);
|
---|
| 578 | });
|
---|
| 579 |
|
---|
| 580 | // determine if expression is constant
|
---|
| 581 | (function(def_is_constant_expression) {
|
---|
| 582 | function all_refs_local(scope) {
|
---|
| 583 | let result = true;
|
---|
| 584 | walk(this, node => {
|
---|
| 585 | if (node instanceof AST_SymbolRef) {
|
---|
| 586 | if (has_flag(this, INLINED)) {
|
---|
| 587 | result = false;
|
---|
| 588 | return walk_abort;
|
---|
| 589 | }
|
---|
| 590 | var def = node.definition();
|
---|
| 591 | if (
|
---|
| 592 | member(def, this.enclosed)
|
---|
| 593 | && !this.variables.has(def.name)
|
---|
| 594 | ) {
|
---|
| 595 | if (scope) {
|
---|
| 596 | var scope_def = scope.find_variable(node);
|
---|
| 597 | if (def.undeclared ? !scope_def : scope_def === def) {
|
---|
| 598 | result = "f";
|
---|
| 599 | return true;
|
---|
| 600 | }
|
---|
| 601 | }
|
---|
| 602 | result = false;
|
---|
| 603 | return walk_abort;
|
---|
| 604 | }
|
---|
| 605 | return true;
|
---|
| 606 | }
|
---|
| 607 | if (node instanceof AST_This && this instanceof AST_Arrow) {
|
---|
| 608 | result = false;
|
---|
| 609 | return walk_abort;
|
---|
| 610 | }
|
---|
| 611 | });
|
---|
| 612 | return result;
|
---|
| 613 | }
|
---|
| 614 |
|
---|
| 615 | def_is_constant_expression(AST_Node, return_false);
|
---|
| 616 | def_is_constant_expression(AST_Constant, return_true);
|
---|
| 617 | def_is_constant_expression(AST_Class, function(scope) {
|
---|
| 618 | if (this.extends && !this.extends.is_constant_expression(scope)) {
|
---|
| 619 | return false;
|
---|
| 620 | }
|
---|
| 621 |
|
---|
| 622 | for (const prop of this.properties) {
|
---|
| 623 | if (prop.computed_key() && !prop.key.is_constant_expression(scope)) {
|
---|
| 624 | return false;
|
---|
| 625 | }
|
---|
| 626 | if (prop.static && prop.value && !prop.value.is_constant_expression(scope)) {
|
---|
| 627 | return false;
|
---|
| 628 | }
|
---|
| 629 | if (prop instanceof AST_ClassStaticBlock) {
|
---|
| 630 | return false;
|
---|
| 631 | }
|
---|
| 632 | }
|
---|
| 633 |
|
---|
| 634 | return all_refs_local.call(this, scope);
|
---|
| 635 | });
|
---|
| 636 | def_is_constant_expression(AST_Lambda, all_refs_local);
|
---|
| 637 | def_is_constant_expression(AST_Unary, function() {
|
---|
| 638 | return this.expression.is_constant_expression();
|
---|
| 639 | });
|
---|
| 640 | def_is_constant_expression(AST_Binary, function() {
|
---|
| 641 | return this.left.is_constant_expression()
|
---|
| 642 | && this.right.is_constant_expression();
|
---|
| 643 | });
|
---|
| 644 | def_is_constant_expression(AST_Array, function() {
|
---|
| 645 | return this.elements.every((l) => l.is_constant_expression());
|
---|
| 646 | });
|
---|
| 647 | def_is_constant_expression(AST_Object, function() {
|
---|
| 648 | return this.properties.every((l) => l.is_constant_expression());
|
---|
| 649 | });
|
---|
| 650 | def_is_constant_expression(AST_ObjectProperty, function() {
|
---|
| 651 | return !!(!(this.key instanceof AST_Node) && this.value && this.value.is_constant_expression());
|
---|
| 652 | });
|
---|
| 653 | })(function(node, func) {
|
---|
| 654 | node.DEFMETHOD("is_constant_expression", func);
|
---|
| 655 | });
|
---|
| 656 |
|
---|
| 657 |
|
---|
| 658 | // may_throw_on_access()
|
---|
| 659 | // returns true if this node may be null, undefined or contain `AST_Accessor`
|
---|
| 660 | (function(def_may_throw_on_access) {
|
---|
| 661 | AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
|
---|
| 662 | return !compressor.option("pure_getters")
|
---|
| 663 | || this._dot_throw(compressor);
|
---|
| 664 | });
|
---|
| 665 |
|
---|
| 666 | function is_strict(compressor) {
|
---|
| 667 | return /strict/.test(compressor.option("pure_getters"));
|
---|
| 668 | }
|
---|
| 669 |
|
---|
| 670 | def_may_throw_on_access(AST_Node, is_strict);
|
---|
| 671 | def_may_throw_on_access(AST_Null, return_true);
|
---|
| 672 | def_may_throw_on_access(AST_Undefined, return_true);
|
---|
| 673 | def_may_throw_on_access(AST_Constant, return_false);
|
---|
| 674 | def_may_throw_on_access(AST_Array, return_false);
|
---|
| 675 | def_may_throw_on_access(AST_Object, function(compressor) {
|
---|
| 676 | if (!is_strict(compressor)) return false;
|
---|
| 677 | for (var i = this.properties.length; --i >=0;)
|
---|
| 678 | if (this.properties[i]._dot_throw(compressor)) return true;
|
---|
| 679 | return false;
|
---|
| 680 | });
|
---|
| 681 | // Do not be as strict with classes as we are with objects.
|
---|
| 682 | // Hopefully the community is not going to abuse static getters and setters.
|
---|
| 683 | // https://github.com/terser/terser/issues/724#issuecomment-643655656
|
---|
| 684 | def_may_throw_on_access(AST_Class, return_false);
|
---|
| 685 | def_may_throw_on_access(AST_ObjectProperty, return_false);
|
---|
| 686 | def_may_throw_on_access(AST_ObjectGetter, return_true);
|
---|
| 687 | def_may_throw_on_access(AST_Expansion, function(compressor) {
|
---|
| 688 | return this.expression._dot_throw(compressor);
|
---|
| 689 | });
|
---|
| 690 | def_may_throw_on_access(AST_Function, return_false);
|
---|
| 691 | def_may_throw_on_access(AST_Arrow, return_false);
|
---|
| 692 | def_may_throw_on_access(AST_UnaryPostfix, return_false);
|
---|
| 693 | def_may_throw_on_access(AST_UnaryPrefix, function() {
|
---|
| 694 | return this.operator == "void";
|
---|
| 695 | });
|
---|
| 696 | def_may_throw_on_access(AST_Binary, function(compressor) {
|
---|
| 697 | return (this.operator == "&&" || this.operator == "||" || this.operator == "??")
|
---|
| 698 | && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
|
---|
| 699 | });
|
---|
| 700 | def_may_throw_on_access(AST_Assign, function(compressor) {
|
---|
| 701 | if (this.logical) return true;
|
---|
| 702 |
|
---|
| 703 | return this.operator == "="
|
---|
| 704 | && this.right._dot_throw(compressor);
|
---|
| 705 | });
|
---|
| 706 | def_may_throw_on_access(AST_Conditional, function(compressor) {
|
---|
| 707 | return this.consequent._dot_throw(compressor)
|
---|
| 708 | || this.alternative._dot_throw(compressor);
|
---|
| 709 | });
|
---|
| 710 | def_may_throw_on_access(AST_Dot, function(compressor) {
|
---|
| 711 | if (!is_strict(compressor)) return false;
|
---|
| 712 |
|
---|
| 713 | if (this.property == "prototype") {
|
---|
| 714 | return !(
|
---|
| 715 | this.expression instanceof AST_Function
|
---|
| 716 | || this.expression instanceof AST_Class
|
---|
| 717 | );
|
---|
| 718 | }
|
---|
| 719 | return true;
|
---|
| 720 | });
|
---|
| 721 | def_may_throw_on_access(AST_Chain, function(compressor) {
|
---|
| 722 | return this.expression._dot_throw(compressor);
|
---|
| 723 | });
|
---|
| 724 | def_may_throw_on_access(AST_Sequence, function(compressor) {
|
---|
| 725 | return this.tail_node()._dot_throw(compressor);
|
---|
| 726 | });
|
---|
| 727 | def_may_throw_on_access(AST_SymbolRef, function(compressor) {
|
---|
| 728 | if (this.name === "arguments" && this.scope instanceof AST_Lambda) return false;
|
---|
| 729 | if (has_flag(this, UNDEFINED)) return true;
|
---|
| 730 | if (!is_strict(compressor)) return false;
|
---|
| 731 | if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
|
---|
| 732 | if (this.is_immutable()) return false;
|
---|
| 733 | var fixed = this.fixed_value();
|
---|
| 734 | return !fixed || fixed._dot_throw(compressor);
|
---|
| 735 | });
|
---|
| 736 | })(function(node, func) {
|
---|
| 737 | node.DEFMETHOD("_dot_throw", func);
|
---|
| 738 | });
|
---|
| 739 |
|
---|
| 740 | export function is_lhs(node, parent) {
|
---|
| 741 | if (parent instanceof AST_Unary && unary_side_effects.has(parent.operator)) return parent.expression;
|
---|
| 742 | if (parent instanceof AST_Assign && parent.left === node) return node;
|
---|
| 743 | if (parent instanceof AST_ForIn && parent.init === node) return node;
|
---|
| 744 | }
|
---|
| 745 |
|
---|
| 746 | // method to negate an expression
|
---|
| 747 | (function(def_negate) {
|
---|
| 748 | function basic_negation(exp) {
|
---|
| 749 | return make_node(AST_UnaryPrefix, exp, {
|
---|
| 750 | operator: "!",
|
---|
| 751 | expression: exp
|
---|
| 752 | });
|
---|
| 753 | }
|
---|
| 754 | function best(orig, alt, first_in_statement) {
|
---|
| 755 | var negated = basic_negation(orig);
|
---|
| 756 | if (first_in_statement) {
|
---|
| 757 | var stat = make_node(AST_SimpleStatement, alt, {
|
---|
| 758 | body: alt
|
---|
| 759 | });
|
---|
| 760 | return best_of_expression(negated, stat) === stat ? alt : negated;
|
---|
| 761 | }
|
---|
| 762 | return best_of_expression(negated, alt);
|
---|
| 763 | }
|
---|
| 764 | def_negate(AST_Node, function() {
|
---|
| 765 | return basic_negation(this);
|
---|
| 766 | });
|
---|
| 767 | def_negate(AST_Statement, function() {
|
---|
| 768 | throw new Error("Cannot negate a statement");
|
---|
| 769 | });
|
---|
| 770 | def_negate(AST_Function, function() {
|
---|
| 771 | return basic_negation(this);
|
---|
| 772 | });
|
---|
| 773 | def_negate(AST_Class, function() {
|
---|
| 774 | return basic_negation(this);
|
---|
| 775 | });
|
---|
| 776 | def_negate(AST_Arrow, function() {
|
---|
| 777 | return basic_negation(this);
|
---|
| 778 | });
|
---|
| 779 | def_negate(AST_UnaryPrefix, function() {
|
---|
| 780 | if (this.operator == "!")
|
---|
| 781 | return this.expression;
|
---|
| 782 | return basic_negation(this);
|
---|
| 783 | });
|
---|
| 784 | def_negate(AST_Sequence, function(compressor) {
|
---|
| 785 | var expressions = this.expressions.slice();
|
---|
| 786 | expressions.push(expressions.pop().negate(compressor));
|
---|
| 787 | return make_sequence(this, expressions);
|
---|
| 788 | });
|
---|
| 789 | def_negate(AST_Conditional, function(compressor, first_in_statement) {
|
---|
| 790 | var self = this.clone();
|
---|
| 791 | self.consequent = self.consequent.negate(compressor);
|
---|
| 792 | self.alternative = self.alternative.negate(compressor);
|
---|
| 793 | return best(this, self, first_in_statement);
|
---|
| 794 | });
|
---|
| 795 | def_negate(AST_Binary, function(compressor, first_in_statement) {
|
---|
| 796 | var self = this.clone(), op = this.operator;
|
---|
| 797 | if (compressor.option("unsafe_comps")) {
|
---|
| 798 | switch (op) {
|
---|
| 799 | case "<=" : self.operator = ">" ; return self;
|
---|
| 800 | case "<" : self.operator = ">=" ; return self;
|
---|
| 801 | case ">=" : self.operator = "<" ; return self;
|
---|
| 802 | case ">" : self.operator = "<=" ; return self;
|
---|
| 803 | }
|
---|
| 804 | }
|
---|
| 805 | switch (op) {
|
---|
| 806 | case "==" : self.operator = "!="; return self;
|
---|
| 807 | case "!=" : self.operator = "=="; return self;
|
---|
| 808 | case "===": self.operator = "!=="; return self;
|
---|
| 809 | case "!==": self.operator = "==="; return self;
|
---|
| 810 | case "&&":
|
---|
| 811 | self.operator = "||";
|
---|
| 812 | self.left = self.left.negate(compressor, first_in_statement);
|
---|
| 813 | self.right = self.right.negate(compressor);
|
---|
| 814 | return best(this, self, first_in_statement);
|
---|
| 815 | case "||":
|
---|
| 816 | self.operator = "&&";
|
---|
| 817 | self.left = self.left.negate(compressor, first_in_statement);
|
---|
| 818 | self.right = self.right.negate(compressor);
|
---|
| 819 | return best(this, self, first_in_statement);
|
---|
| 820 | }
|
---|
| 821 | return basic_negation(this);
|
---|
| 822 | });
|
---|
| 823 | })(function(node, func) {
|
---|
| 824 | node.DEFMETHOD("negate", function(compressor, first_in_statement) {
|
---|
| 825 | return func.call(this, compressor, first_in_statement);
|
---|
| 826 | });
|
---|
| 827 | });
|
---|
| 828 |
|
---|
| 829 | (function (def_bitwise_negate) {
|
---|
| 830 | function basic_negation(exp) {
|
---|
| 831 | return make_node(AST_UnaryPrefix, exp, {
|
---|
| 832 | operator: "~",
|
---|
| 833 | expression: exp
|
---|
| 834 | });
|
---|
| 835 | }
|
---|
| 836 |
|
---|
| 837 | def_bitwise_negate(AST_Node, function() {
|
---|
| 838 | return basic_negation(this);
|
---|
| 839 | });
|
---|
| 840 |
|
---|
| 841 | def_bitwise_negate(AST_Number, function() {
|
---|
| 842 | const neg = ~this.value;
|
---|
| 843 | if (neg.toString().length > this.value.toString().length) {
|
---|
| 844 | return basic_negation(this);
|
---|
| 845 | }
|
---|
| 846 | return make_node(AST_Number, this, { value: neg });
|
---|
| 847 | });
|
---|
| 848 |
|
---|
| 849 | def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) {
|
---|
| 850 | if (this.operator == "~" && (in_32_bit_context || this.expression.is_32_bit_integer())) {
|
---|
| 851 | return this.expression;
|
---|
| 852 | } else {
|
---|
| 853 | return basic_negation(this);
|
---|
| 854 | }
|
---|
| 855 | });
|
---|
| 856 | })(function (node, func) {
|
---|
| 857 | node.DEFMETHOD("bitwise_negate", func);
|
---|
| 858 | });
|
---|
| 859 |
|
---|
| 860 | // Is the callee of this function pure?
|
---|
| 861 | 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");
|
---|
| 862 | AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
|
---|
| 863 | if (compressor.option("unsafe")) {
|
---|
| 864 | var expr = this.expression;
|
---|
| 865 | var first_arg = (this.args && this.args[0] && this.args[0].evaluate(compressor));
|
---|
| 866 | if (
|
---|
| 867 | expr.expression && expr.expression.name === "hasOwnProperty" &&
|
---|
| 868 | (first_arg == null || first_arg.thedef && first_arg.thedef.undeclared)
|
---|
| 869 | ) {
|
---|
| 870 | return false;
|
---|
| 871 | }
|
---|
| 872 | if (is_undeclared_ref(expr) && global_pure_fns.has(expr.name)) return true;
|
---|
| 873 | if (
|
---|
| 874 | expr instanceof AST_Dot
|
---|
| 875 | && is_undeclared_ref(expr.expression)
|
---|
| 876 | && is_pure_native_fn(expr.expression.name, expr.property)
|
---|
| 877 | ) {
|
---|
| 878 | return true;
|
---|
| 879 | }
|
---|
| 880 | }
|
---|
| 881 | if ((this instanceof AST_New) && compressor.option("pure_new")) {
|
---|
| 882 | return true;
|
---|
| 883 | }
|
---|
| 884 | if (compressor.option("side_effects") && has_annotation(this, _PURE)) {
|
---|
| 885 | return true;
|
---|
| 886 | }
|
---|
| 887 | return !compressor.pure_funcs(this);
|
---|
| 888 | });
|
---|
| 889 |
|
---|
| 890 | // If I call this, is it a pure function?
|
---|
| 891 | AST_Node.DEFMETHOD("is_call_pure", return_false);
|
---|
| 892 | AST_Dot.DEFMETHOD("is_call_pure", function(compressor) {
|
---|
| 893 | if (!compressor.option("unsafe")) return;
|
---|
| 894 | const expr = this.expression;
|
---|
| 895 |
|
---|
| 896 | let native_obj;
|
---|
| 897 | if (expr instanceof AST_Array) {
|
---|
| 898 | native_obj = "Array";
|
---|
| 899 | } else if (expr.is_boolean()) {
|
---|
| 900 | native_obj = "Boolean";
|
---|
| 901 | } else if (expr.is_number(compressor)) {
|
---|
| 902 | native_obj = "Number";
|
---|
| 903 | } else if (expr instanceof AST_RegExp) {
|
---|
| 904 | native_obj = "RegExp";
|
---|
| 905 | } else if (expr.is_string(compressor)) {
|
---|
| 906 | native_obj = "String";
|
---|
| 907 | } else if (!this.may_throw_on_access(compressor)) {
|
---|
| 908 | native_obj = "Object";
|
---|
| 909 | }
|
---|
| 910 | return native_obj != null && is_pure_native_method(native_obj, this.property);
|
---|
| 911 | });
|
---|
| 912 |
|
---|
| 913 | // tell me if a statement aborts
|
---|
| 914 | export const aborts = (thing) => thing && thing.aborts();
|
---|
| 915 |
|
---|
| 916 | (function(def_aborts) {
|
---|
| 917 | def_aborts(AST_Statement, return_null);
|
---|
| 918 | def_aborts(AST_Jump, return_this);
|
---|
| 919 | function block_aborts() {
|
---|
| 920 | for (var i = 0; i < this.body.length; i++) {
|
---|
| 921 | if (aborts(this.body[i])) {
|
---|
| 922 | return this.body[i];
|
---|
| 923 | }
|
---|
| 924 | }
|
---|
| 925 | return null;
|
---|
| 926 | }
|
---|
| 927 | def_aborts(AST_Import, return_null);
|
---|
| 928 | def_aborts(AST_BlockStatement, block_aborts);
|
---|
| 929 | def_aborts(AST_SwitchBranch, block_aborts);
|
---|
| 930 | def_aborts(AST_DefClass, function () {
|
---|
| 931 | for (const prop of this.properties) {
|
---|
| 932 | if (prop instanceof AST_ClassStaticBlock) {
|
---|
| 933 | if (prop.aborts()) return prop;
|
---|
| 934 | }
|
---|
| 935 | }
|
---|
| 936 | return null;
|
---|
| 937 | });
|
---|
| 938 | def_aborts(AST_ClassStaticBlock, block_aborts);
|
---|
| 939 | def_aborts(AST_If, function() {
|
---|
| 940 | return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
|
---|
| 941 | });
|
---|
| 942 | })(function(node, func) {
|
---|
| 943 | node.DEFMETHOD("aborts", func);
|
---|
| 944 | });
|
---|
| 945 |
|
---|
| 946 | AST_Node.DEFMETHOD("contains_this", function() {
|
---|
| 947 | return walk(this, node => {
|
---|
| 948 | if (node instanceof AST_This) return walk_abort;
|
---|
| 949 | if (
|
---|
| 950 | node !== this
|
---|
| 951 | && node instanceof AST_Scope
|
---|
| 952 | && !(node instanceof AST_Arrow)
|
---|
| 953 | ) {
|
---|
| 954 | return true;
|
---|
| 955 | }
|
---|
| 956 | });
|
---|
| 957 | });
|
---|
| 958 |
|
---|
| 959 | export function is_modified(compressor, tw, node, value, level, immutable) {
|
---|
| 960 | var parent = tw.parent(level);
|
---|
| 961 | var lhs = is_lhs(node, parent);
|
---|
| 962 | if (lhs) return lhs;
|
---|
| 963 | if (!immutable
|
---|
| 964 | && parent instanceof AST_Call
|
---|
| 965 | && parent.expression === node
|
---|
| 966 | && !(value instanceof AST_Arrow)
|
---|
| 967 | && !(value instanceof AST_Class)
|
---|
| 968 | && !parent.is_callee_pure(compressor)
|
---|
| 969 | && (!(value instanceof AST_Function)
|
---|
| 970 | || !(parent instanceof AST_New) && value.contains_this())) {
|
---|
| 971 | return true;
|
---|
| 972 | }
|
---|
| 973 | if (parent instanceof AST_Array) {
|
---|
| 974 | return is_modified(compressor, tw, parent, parent, level + 1);
|
---|
| 975 | }
|
---|
| 976 | if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
|
---|
| 977 | var obj = tw.parent(level + 1);
|
---|
| 978 | return is_modified(compressor, tw, obj, obj, level + 2);
|
---|
| 979 | }
|
---|
| 980 | if (parent instanceof AST_PropAccess && parent.expression === node) {
|
---|
| 981 | var prop = read_property(value, parent.property);
|
---|
| 982 | return !immutable && is_modified(compressor, tw, parent, prop, level + 1);
|
---|
| 983 | }
|
---|
| 984 | }
|
---|
| 985 |
|
---|
| 986 | /**
|
---|
| 987 | * Check if a node may be used by the expression it's in
|
---|
| 988 | * void (0, 1, {node}, 2) -> false
|
---|
| 989 | * console.log(0, {node}) -> true
|
---|
| 990 | */
|
---|
| 991 | export function is_used_in_expression(tw) {
|
---|
| 992 | for (let p = -1, node, parent; node = tw.parent(p), parent = tw.parent(p + 1); p++) {
|
---|
| 993 | if (parent instanceof AST_Sequence) {
|
---|
| 994 | const nth_expression = parent.expressions.indexOf(node);
|
---|
| 995 | if (nth_expression !== parent.expressions.length - 1) {
|
---|
| 996 | // Detect (0, x.noThis)() constructs
|
---|
| 997 | const grandparent = tw.parent(p + 2);
|
---|
| 998 | if (
|
---|
| 999 | parent.expressions.length > 2
|
---|
| 1000 | || parent.expressions.length === 1
|
---|
| 1001 | || !requires_sequence_to_maintain_binding(grandparent, parent, parent.expressions[1])
|
---|
| 1002 | ) {
|
---|
| 1003 | return false;
|
---|
| 1004 | }
|
---|
| 1005 | return true;
|
---|
| 1006 | } else {
|
---|
| 1007 | continue;
|
---|
| 1008 | }
|
---|
| 1009 | }
|
---|
| 1010 | if (parent instanceof AST_Unary) {
|
---|
| 1011 | const op = parent.operator;
|
---|
| 1012 | if (op === "void") {
|
---|
| 1013 | return false;
|
---|
| 1014 | }
|
---|
| 1015 | if (op === "typeof" || op === "+" || op === "-" || op === "!" || op === "~") {
|
---|
| 1016 | continue;
|
---|
| 1017 | }
|
---|
| 1018 | }
|
---|
| 1019 | if (
|
---|
| 1020 | parent instanceof AST_SimpleStatement
|
---|
| 1021 | || parent instanceof AST_LabeledStatement
|
---|
| 1022 | ) {
|
---|
| 1023 | return false;
|
---|
| 1024 | }
|
---|
| 1025 | if (parent instanceof AST_Scope) {
|
---|
| 1026 | return false;
|
---|
| 1027 | }
|
---|
| 1028 | return true;
|
---|
| 1029 | }
|
---|
| 1030 |
|
---|
| 1031 | return true;
|
---|
| 1032 | }
|
---|