source: imaps-frontend/node_modules/terser/lib/compress/common.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 11.6 KB
Line 
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
44import {
45 AST_Array,
46 AST_Arrow,
47 AST_BigInt,
48 AST_BlockStatement,
49 AST_Call,
50 AST_Chain,
51 AST_Class,
52 AST_Const,
53 AST_Constant,
54 AST_DefClass,
55 AST_Defun,
56 AST_EmptyStatement,
57 AST_Export,
58 AST_False,
59 AST_Function,
60 AST_Import,
61 AST_Infinity,
62 AST_LabeledStatement,
63 AST_Lambda,
64 AST_Let,
65 AST_LoopControl,
66 AST_NaN,
67 AST_Node,
68 AST_Null,
69 AST_Number,
70 AST_Object,
71 AST_ObjectKeyVal,
72 AST_PropAccess,
73 AST_RegExp,
74 AST_Scope,
75 AST_Sequence,
76 AST_SimpleStatement,
77 AST_Statement,
78 AST_String,
79 AST_SymbolRef,
80 AST_True,
81 AST_UnaryPrefix,
82 AST_Undefined,
83
84 TreeWalker,
85 walk,
86 walk_abort,
87 walk_parent,
88} from "../ast.js";
89import { make_node, regexp_source_fix, string_template, makePredicate } from "../utils/index.js";
90import { first_in_statement } from "../utils/first_in_statement.js";
91import { has_flag, TOP } from "./compressor-flags.js";
92
93export function merge_sequence(array, node) {
94 if (node instanceof AST_Sequence) {
95 array.push(...node.expressions);
96 } else {
97 array.push(node);
98 }
99 return array;
100}
101
102export function make_sequence(orig, expressions) {
103 if (expressions.length == 1) return expressions[0];
104 if (expressions.length == 0) throw new Error("trying to create a sequence with length zero!");
105 return make_node(AST_Sequence, orig, {
106 expressions: expressions.reduce(merge_sequence, [])
107 });
108}
109
110export function make_empty_function(self) {
111 return make_node(AST_Function, self, {
112 uses_arguments: false,
113 argnames: [],
114 body: [],
115 is_generator: false,
116 async: false,
117 variables: new Map(),
118 uses_with: false,
119 uses_eval: false,
120 parent_scope: null,
121 enclosed: [],
122 cname: 0,
123 block_scope: undefined,
124 });
125}
126
127export function make_node_from_constant(val, orig) {
128 switch (typeof val) {
129 case "string":
130 return make_node(AST_String, orig, {
131 value: val
132 });
133 case "number":
134 if (isNaN(val)) return make_node(AST_NaN, orig);
135 if (isFinite(val)) {
136 return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
137 operator: "-",
138 expression: make_node(AST_Number, orig, { value: -val })
139 }) : make_node(AST_Number, orig, { value: val });
140 }
141 return val < 0 ? make_node(AST_UnaryPrefix, orig, {
142 operator: "-",
143 expression: make_node(AST_Infinity, orig)
144 }) : make_node(AST_Infinity, orig);
145 case "bigint":
146 return make_node(AST_BigInt, orig, { value: val.toString() });
147 case "boolean":
148 return make_node(val ? AST_True : AST_False, orig);
149 case "undefined":
150 return make_node(AST_Undefined, orig);
151 default:
152 if (val === null) {
153 return make_node(AST_Null, orig, { value: null });
154 }
155 if (val instanceof RegExp) {
156 return make_node(AST_RegExp, orig, {
157 value: {
158 source: regexp_source_fix(val.source),
159 flags: val.flags
160 }
161 });
162 }
163 throw new Error(string_template("Can't handle constant of type: {type}", {
164 type: typeof val
165 }));
166 }
167}
168
169export function best_of_expression(ast1, ast2) {
170 return ast1.size() > ast2.size() ? ast2 : ast1;
171}
172
173export function best_of_statement(ast1, ast2) {
174 return best_of_expression(
175 make_node(AST_SimpleStatement, ast1, {
176 body: ast1
177 }),
178 make_node(AST_SimpleStatement, ast2, {
179 body: ast2
180 })
181 ).body;
182}
183
184/** Find which node is smaller, and return that */
185export function best_of(compressor, ast1, ast2) {
186 if (first_in_statement(compressor)) {
187 return best_of_statement(ast1, ast2);
188 } else {
189 return best_of_expression(ast1, ast2);
190 }
191}
192
193/** Simplify an object property's key, if possible */
194export function get_simple_key(key) {
195 if (key instanceof AST_Constant) {
196 return key.getValue();
197 }
198 if (key instanceof AST_UnaryPrefix
199 && key.operator == "void"
200 && key.expression instanceof AST_Constant) {
201 return;
202 }
203 return key;
204}
205
206export function read_property(obj, key) {
207 key = get_simple_key(key);
208 if (key instanceof AST_Node) return;
209
210 var value;
211 if (obj instanceof AST_Array) {
212 var elements = obj.elements;
213 if (key == "length") return make_node_from_constant(elements.length, obj);
214 if (typeof key == "number" && key in elements) value = elements[key];
215 } else if (obj instanceof AST_Object) {
216 key = "" + key;
217 var props = obj.properties;
218 for (var i = props.length; --i >= 0;) {
219 var prop = props[i];
220 if (!(prop instanceof AST_ObjectKeyVal)) return;
221 if (!value && props[i].key === key) value = props[i].value;
222 }
223 }
224
225 return value instanceof AST_SymbolRef && value.fixed_value() || value;
226}
227
228export function has_break_or_continue(loop, parent) {
229 var found = false;
230 var tw = new TreeWalker(function(node) {
231 if (found || node instanceof AST_Scope) return true;
232 if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === loop) {
233 return found = true;
234 }
235 });
236 if (parent instanceof AST_LabeledStatement) tw.push(parent);
237 tw.push(loop);
238 loop.body.walk(tw);
239 return found;
240}
241
242// we shouldn't compress (1,func)(something) to
243// func(something) because that changes the meaning of
244// the func (becomes lexical instead of global).
245export function maintain_this_binding(parent, orig, val) {
246 if (requires_sequence_to_maintain_binding(parent, orig, val)) {
247 const zero = make_node(AST_Number, orig, { value: 0 });
248 return make_sequence(orig, [ zero, val ]);
249 } else {
250 return val;
251 }
252}
253
254/** Detect (1, x.noThis)(), (0, eval)(), which need sequences */
255export function requires_sequence_to_maintain_binding(parent, orig, val) {
256 return (
257 parent instanceof AST_UnaryPrefix && parent.operator == "delete"
258 || parent instanceof AST_Call && parent.expression === orig
259 && (
260 val instanceof AST_Chain
261 || val instanceof AST_PropAccess
262 || val instanceof AST_SymbolRef && val.name == "eval"
263 )
264 );
265}
266
267export function is_func_expr(node) {
268 return node instanceof AST_Arrow || node instanceof AST_Function;
269}
270
271/**
272 * Used to determine whether the node can benefit from negation.
273 * Not the case with arrow functions (you need an extra set of parens). */
274export function is_iife_call(node) {
275 if (node.TYPE != "Call") return false;
276 return node.expression instanceof AST_Function || is_iife_call(node.expression);
277}
278
279export function is_empty(thing) {
280 if (thing === null) return true;
281 if (thing instanceof AST_EmptyStatement) return true;
282 if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
283 return false;
284}
285
286export const identifier_atom = makePredicate("Infinity NaN undefined");
287export function is_identifier_atom(node) {
288 return node instanceof AST_Infinity
289 || node instanceof AST_NaN
290 || node instanceof AST_Undefined;
291}
292
293/** Check if this is a SymbolRef node which has one def of a certain AST type */
294export function is_ref_of(ref, type) {
295 if (!(ref instanceof AST_SymbolRef)) return false;
296 var orig = ref.definition().orig;
297 for (var i = orig.length; --i >= 0;) {
298 if (orig[i] instanceof type) return true;
299 }
300}
301
302/**Can we turn { block contents... } into just the block contents ?
303 * Not if one of these is inside.
304 **/
305export function can_be_evicted_from_block(node) {
306 return !(
307 node instanceof AST_DefClass ||
308 node instanceof AST_Defun ||
309 node instanceof AST_Let ||
310 node instanceof AST_Const ||
311 node instanceof AST_Export ||
312 node instanceof AST_Import
313 );
314}
315
316export function as_statement_array(thing) {
317 if (thing === null) return [];
318 if (thing instanceof AST_BlockStatement) return thing.body;
319 if (thing instanceof AST_EmptyStatement) return [];
320 if (thing instanceof AST_Statement) return [ thing ];
321 throw new Error("Can't convert thing to statement array");
322}
323
324export function is_reachable(scope_node, defs) {
325 const find_ref = node => {
326 if (node instanceof AST_SymbolRef && defs.includes(node.definition())) {
327 return walk_abort;
328 }
329 };
330
331 return walk_parent(scope_node, (node, info) => {
332 if (node instanceof AST_Scope && node !== scope_node) {
333 var parent = info.parent();
334
335 if (
336 parent instanceof AST_Call
337 && parent.expression === node
338 // Async/Generators aren't guaranteed to sync evaluate all of
339 // their body steps, so it's possible they close over the variable.
340 && !(node.async || node.is_generator)
341 ) {
342 return;
343 }
344
345 if (walk(node, find_ref)) return walk_abort;
346
347 return true;
348 }
349 });
350}
351
352/** Check if a ref refers to the name of a function/class it's defined within */
353export function is_recursive_ref(tw, def) {
354 var node;
355 for (var i = 0; node = tw.parent(i); i++) {
356 if (node instanceof AST_Lambda || node instanceof AST_Class) {
357 var name = node.name;
358 if (name && name.definition() === def) {
359 return true;
360 }
361 }
362 }
363 return false;
364}
365
366// TODO this only works with AST_Defun, shouldn't it work for other ways of defining functions?
367export function retain_top_func(fn, compressor) {
368 return compressor.top_retain
369 && fn instanceof AST_Defun
370 && has_flag(fn, TOP)
371 && fn.name
372 && compressor.top_retain(fn.name.definition());
373}
Note: See TracBrowser for help on using the repository browser.