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 | }
|
---|