source: trip-planner-front/node_modules/terser/lib/scope.js

Last change on this file was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 32.5 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
44"use strict";
45
46import {
47 defaults,
48 keep_name,
49 mergeSort,
50 push_uniq,
51 make_node,
52 return_false,
53 return_this,
54 return_true,
55 string_template,
56} from "./utils/index.js";
57import {
58 AST_Arrow,
59 AST_Block,
60 AST_Call,
61 AST_Catch,
62 AST_Class,
63 AST_Conditional,
64 AST_DefClass,
65 AST_Defun,
66 AST_Destructuring,
67 AST_Dot,
68 AST_DotHash,
69 AST_Export,
70 AST_For,
71 AST_ForIn,
72 AST_Function,
73 AST_Import,
74 AST_IterationStatement,
75 AST_Label,
76 AST_LabeledStatement,
77 AST_LabelRef,
78 AST_Lambda,
79 AST_LoopControl,
80 AST_NameMapping,
81 AST_Node,
82 AST_Scope,
83 AST_Sequence,
84 AST_String,
85 AST_Sub,
86 AST_Switch,
87 AST_SwitchBranch,
88 AST_Symbol,
89 AST_SymbolBlockDeclaration,
90 AST_SymbolCatch,
91 AST_SymbolClass,
92 AST_SymbolConst,
93 AST_SymbolDefClass,
94 AST_SymbolDefun,
95 AST_SymbolExport,
96 AST_SymbolFunarg,
97 AST_SymbolImport,
98 AST_SymbolLambda,
99 AST_SymbolLet,
100 AST_SymbolMethod,
101 AST_SymbolRef,
102 AST_SymbolVar,
103 AST_Toplevel,
104 AST_VarDef,
105 AST_With,
106 TreeWalker,
107 walk
108} from "./ast.js";
109import {
110 RESERVED_WORDS,
111 js_error,
112} from "./parse.js";
113
114const MASK_EXPORT_DONT_MANGLE = 1 << 0;
115const MASK_EXPORT_WANT_MANGLE = 1 << 1;
116
117let function_defs = null;
118let unmangleable_names = null;
119
120class SymbolDef {
121 constructor(scope, orig, init) {
122 this.name = orig.name;
123 this.orig = [ orig ];
124 this.init = init;
125 this.eliminated = 0;
126 this.assignments = 0;
127 this.scope = scope;
128 this.replaced = 0;
129 this.global = false;
130 this.export = 0;
131 this.mangled_name = null;
132 this.undeclared = false;
133 this.id = SymbolDef.next_id++;
134 this.chained = false;
135 this.direct_access = false;
136 this.escaped = 0;
137 this.recursive_refs = 0;
138 this.references = [];
139 this.should_replace = undefined;
140 this.single_use = false;
141 this.fixed = false;
142 Object.seal(this);
143 }
144 fixed_value() {
145 if (!this.fixed || this.fixed instanceof AST_Node) return this.fixed;
146 return this.fixed();
147 }
148 unmangleable(options) {
149 if (!options) options = {};
150
151 if (
152 function_defs &&
153 function_defs.has(this.id) &&
154 keep_name(options.keep_fnames, this.orig[0].name)
155 ) return true;
156
157 return this.global && !options.toplevel
158 || (this.export & MASK_EXPORT_DONT_MANGLE)
159 || this.undeclared
160 || !options.eval && this.scope.pinned()
161 || (this.orig[0] instanceof AST_SymbolLambda
162 || this.orig[0] instanceof AST_SymbolDefun) && keep_name(options.keep_fnames, this.orig[0].name)
163 || this.orig[0] instanceof AST_SymbolMethod
164 || (this.orig[0] instanceof AST_SymbolClass
165 || this.orig[0] instanceof AST_SymbolDefClass) && keep_name(options.keep_classnames, this.orig[0].name);
166 }
167 mangle(options) {
168 const cache = options.cache && options.cache.props;
169 if (this.global && cache && cache.has(this.name)) {
170 this.mangled_name = cache.get(this.name);
171 } else if (!this.mangled_name && !this.unmangleable(options)) {
172 var s = this.scope;
173 var sym = this.orig[0];
174 if (options.ie8 && sym instanceof AST_SymbolLambda)
175 s = s.parent_scope;
176 const redefinition = redefined_catch_def(this);
177 this.mangled_name = redefinition
178 ? redefinition.mangled_name || redefinition.name
179 : s.next_mangled(options, this);
180 if (this.global && cache) {
181 cache.set(this.name, this.mangled_name);
182 }
183 }
184 }
185}
186
187SymbolDef.next_id = 1;
188
189function redefined_catch_def(def) {
190 if (def.orig[0] instanceof AST_SymbolCatch
191 && def.scope.is_block_scope()
192 ) {
193 return def.scope.get_defun_scope().variables.get(def.name);
194 }
195}
196
197AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope = null, toplevel = this } = {}) {
198 options = defaults(options, {
199 cache: null,
200 ie8: false,
201 safari10: false,
202 });
203
204 if (!(toplevel instanceof AST_Toplevel)) {
205 throw new Error("Invalid toplevel scope");
206 }
207
208 // pass 1: setup scope chaining and handle definitions
209 var scope = this.parent_scope = parent_scope;
210 var labels = new Map();
211 var defun = null;
212 var in_destructuring = null;
213 var for_scopes = [];
214 var tw = new TreeWalker((node, descend) => {
215 if (node.is_block_scope()) {
216 const save_scope = scope;
217 node.block_scope = scope = new AST_Scope(node);
218 scope._block_scope = true;
219 // AST_Try in the AST sadly *is* (not has) a body itself,
220 // and its catch and finally branches are children of the AST_Try itself
221 const parent_scope = node instanceof AST_Catch
222 ? save_scope.parent_scope
223 : save_scope;
224 scope.init_scope_vars(parent_scope);
225 scope.uses_with = save_scope.uses_with;
226 scope.uses_eval = save_scope.uses_eval;
227 if (options.safari10) {
228 if (node instanceof AST_For || node instanceof AST_ForIn) {
229 for_scopes.push(scope);
230 }
231 }
232
233 if (node instanceof AST_Switch) {
234 // XXX: HACK! Ensure the switch expression gets the correct scope (the parent scope) and the body gets the contained scope
235 // AST_Switch has a scope within the body, but it itself "is a block scope"
236 // This means the switched expression has to belong to the outer scope
237 // while the body inside belongs to the switch itself.
238 // This is pretty nasty and warrants an AST change similar to AST_Try (read above)
239 const the_block_scope = scope;
240 scope = save_scope;
241 node.expression.walk(tw);
242 scope = the_block_scope;
243 for (let i = 0; i < node.body.length; i++) {
244 node.body[i].walk(tw);
245 }
246 } else {
247 descend();
248 }
249 scope = save_scope;
250 return true;
251 }
252 if (node instanceof AST_Destructuring) {
253 const save_destructuring = in_destructuring;
254 in_destructuring = node;
255 descend();
256 in_destructuring = save_destructuring;
257 return true;
258 }
259 if (node instanceof AST_Scope) {
260 node.init_scope_vars(scope);
261 var save_scope = scope;
262 var save_defun = defun;
263 var save_labels = labels;
264 defun = scope = node;
265 labels = new Map();
266 descend();
267 scope = save_scope;
268 defun = save_defun;
269 labels = save_labels;
270 return true; // don't descend again in TreeWalker
271 }
272 if (node instanceof AST_LabeledStatement) {
273 var l = node.label;
274 if (labels.has(l.name)) {
275 throw new Error(string_template("Label {name} defined twice", l));
276 }
277 labels.set(l.name, l);
278 descend();
279 labels.delete(l.name);
280 return true; // no descend again
281 }
282 if (node instanceof AST_With) {
283 for (var s = scope; s; s = s.parent_scope)
284 s.uses_with = true;
285 return;
286 }
287 if (node instanceof AST_Symbol) {
288 node.scope = scope;
289 }
290 if (node instanceof AST_Label) {
291 node.thedef = node;
292 node.references = [];
293 }
294 if (node instanceof AST_SymbolLambda) {
295 defun.def_function(node, node.name == "arguments" ? undefined : defun);
296 } else if (node instanceof AST_SymbolDefun) {
297 // Careful here, the scope where this should be defined is
298 // the parent scope. The reason is that we enter a new
299 // scope when we encounter the AST_Defun node (which is
300 // instanceof AST_Scope) but we get to the symbol a bit
301 // later.
302 const closest_scope = defun.parent_scope;
303
304 // In strict mode, function definitions are block-scoped
305 node.scope = tw.directives["use strict"]
306 ? closest_scope
307 : closest_scope.get_defun_scope();
308
309 mark_export(node.scope.def_function(node, defun), 1);
310 } else if (node instanceof AST_SymbolClass) {
311 mark_export(defun.def_variable(node, defun), 1);
312 } else if (node instanceof AST_SymbolImport) {
313 scope.def_variable(node);
314 } else if (node instanceof AST_SymbolDefClass) {
315 // This deals with the name of the class being available
316 // inside the class.
317 mark_export((node.scope = defun.parent_scope).def_function(node, defun), 1);
318 } else if (
319 node instanceof AST_SymbolVar
320 || node instanceof AST_SymbolLet
321 || node instanceof AST_SymbolConst
322 || node instanceof AST_SymbolCatch
323 ) {
324 var def;
325 if (node instanceof AST_SymbolBlockDeclaration) {
326 def = scope.def_variable(node, null);
327 } else {
328 def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
329 }
330 if (!def.orig.every((sym) => {
331 if (sym === node) return true;
332 if (node instanceof AST_SymbolBlockDeclaration) {
333 return sym instanceof AST_SymbolLambda;
334 }
335 return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
336 })) {
337 js_error(
338 `"${node.name}" is redeclared`,
339 node.start.file,
340 node.start.line,
341 node.start.col,
342 node.start.pos
343 );
344 }
345 if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
346 if (defun !== scope) {
347 node.mark_enclosed();
348 var def = scope.find_variable(node);
349 if (node.thedef !== def) {
350 node.thedef = def;
351 node.reference();
352 }
353 }
354 } else if (node instanceof AST_LabelRef) {
355 var sym = labels.get(node.name);
356 if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
357 name: node.name,
358 line: node.start.line,
359 col: node.start.col
360 }));
361 node.thedef = sym;
362 }
363 if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
364 js_error(
365 `"${node.TYPE}" statement may only appear at the top level`,
366 node.start.file,
367 node.start.line,
368 node.start.col,
369 node.start.pos
370 );
371 }
372 });
373 this.walk(tw);
374
375 function mark_export(def, level) {
376 if (in_destructuring) {
377 var i = 0;
378 do {
379 level++;
380 } while (tw.parent(i++) !== in_destructuring);
381 }
382 var node = tw.parent(level);
383 if (def.export = node instanceof AST_Export ? MASK_EXPORT_DONT_MANGLE : 0) {
384 var exported = node.exported_definition;
385 if ((exported instanceof AST_Defun || exported instanceof AST_DefClass) && node.is_default) {
386 def.export = MASK_EXPORT_WANT_MANGLE;
387 }
388 }
389 }
390
391 // pass 2: find back references and eval
392 const is_toplevel = this instanceof AST_Toplevel;
393 if (is_toplevel) {
394 this.globals = new Map();
395 }
396
397 var tw = new TreeWalker(node => {
398 if (node instanceof AST_LoopControl && node.label) {
399 node.label.thedef.references.push(node);
400 return true;
401 }
402 if (node instanceof AST_SymbolRef) {
403 var name = node.name;
404 if (name == "eval" && tw.parent() instanceof AST_Call) {
405 for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
406 s.uses_eval = true;
407 }
408 }
409 var sym;
410 if (tw.parent() instanceof AST_NameMapping && tw.parent(1).module_name
411 || !(sym = node.scope.find_variable(name))) {
412
413 sym = toplevel.def_global(node);
414 if (node instanceof AST_SymbolExport) sym.export = MASK_EXPORT_DONT_MANGLE;
415 } else if (sym.scope instanceof AST_Lambda && name == "arguments") {
416 sym.scope.uses_arguments = true;
417 }
418 node.thedef = sym;
419 node.reference();
420 if (node.scope.is_block_scope()
421 && !(sym.orig[0] instanceof AST_SymbolBlockDeclaration)) {
422 node.scope = node.scope.get_defun_scope();
423 }
424 return true;
425 }
426 // ensure mangling works if catch reuses a scope variable
427 var def;
428 if (node instanceof AST_SymbolCatch && (def = redefined_catch_def(node.definition()))) {
429 var s = node.scope;
430 while (s) {
431 push_uniq(s.enclosed, def);
432 if (s === def.scope) break;
433 s = s.parent_scope;
434 }
435 }
436 });
437 this.walk(tw);
438
439 // pass 3: work around IE8 and Safari catch scope bugs
440 if (options.ie8 || options.safari10) {
441 walk(this, node => {
442 if (node instanceof AST_SymbolCatch) {
443 var name = node.name;
444 var refs = node.thedef.references;
445 var scope = node.scope.get_defun_scope();
446 var def = scope.find_variable(name)
447 || toplevel.globals.get(name)
448 || scope.def_variable(node);
449 refs.forEach(function(ref) {
450 ref.thedef = def;
451 ref.reference();
452 });
453 node.thedef = def;
454 node.reference();
455 return true;
456 }
457 });
458 }
459
460 // pass 4: add symbol definitions to loop scopes
461 // Safari/Webkit bug workaround - loop init let variable shadowing argument.
462 // https://github.com/mishoo/UglifyJS2/issues/1753
463 // https://bugs.webkit.org/show_bug.cgi?id=171041
464 if (options.safari10) {
465 for (const scope of for_scopes) {
466 scope.parent_scope.variables.forEach(function(def) {
467 push_uniq(scope.enclosed, def);
468 });
469 }
470 }
471});
472
473AST_Toplevel.DEFMETHOD("def_global", function(node) {
474 var globals = this.globals, name = node.name;
475 if (globals.has(name)) {
476 return globals.get(name);
477 } else {
478 var g = new SymbolDef(this, node);
479 g.undeclared = true;
480 g.global = true;
481 globals.set(name, g);
482 return g;
483 }
484});
485
486AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
487 this.variables = new Map(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
488 this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
489 this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
490 this.parent_scope = parent_scope; // the parent scope
491 this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
492 this.cname = -1; // the current index for mangling functions/variables
493});
494
495AST_Scope.DEFMETHOD("conflicting_def", function (name) {
496 return (
497 this.enclosed.find(def => def.name === name)
498 || this.variables.has(name)
499 || (this.parent_scope && this.parent_scope.conflicting_def(name))
500 );
501});
502
503AST_Scope.DEFMETHOD("conflicting_def_shallow", function (name) {
504 return (
505 this.enclosed.find(def => def.name === name)
506 || this.variables.has(name)
507 );
508});
509
510AST_Scope.DEFMETHOD("add_child_scope", function (scope) {
511 // `scope` is going to be moved into `this` right now.
512 // Update the required scopes' information
513
514 if (scope.parent_scope === this) return;
515
516 scope.parent_scope = this;
517
518 // TODO uses_with, uses_eval, etc
519
520 const scope_ancestry = (() => {
521 const ancestry = [];
522 let cur = this;
523 do {
524 ancestry.push(cur);
525 } while ((cur = cur.parent_scope));
526 ancestry.reverse();
527 return ancestry;
528 })();
529
530 const new_scope_enclosed_set = new Set(scope.enclosed);
531 const to_enclose = [];
532 for (const scope_topdown of scope_ancestry) {
533 to_enclose.forEach(e => push_uniq(scope_topdown.enclosed, e));
534 for (const def of scope_topdown.variables.values()) {
535 if (new_scope_enclosed_set.has(def)) {
536 push_uniq(to_enclose, def);
537 push_uniq(scope_topdown.enclosed, def);
538 }
539 }
540 }
541});
542
543function find_scopes_visible_from(scopes) {
544 const found_scopes = new Set();
545
546 for (const scope of new Set(scopes)) {
547 (function bubble_up(scope) {
548 if (scope == null || found_scopes.has(scope)) return;
549
550 found_scopes.add(scope);
551
552 bubble_up(scope.parent_scope);
553 })(scope);
554 }
555
556 return [...found_scopes];
557}
558
559// Creates a symbol during compression
560AST_Scope.DEFMETHOD("create_symbol", function(SymClass, {
561 source,
562 tentative_name,
563 scope,
564 conflict_scopes = [scope],
565 init = null
566} = {}) {
567 let symbol_name;
568
569 conflict_scopes = find_scopes_visible_from(conflict_scopes);
570
571 if (tentative_name) {
572 // Implement hygiene (no new names are conflicting with existing names)
573 tentative_name =
574 symbol_name =
575 tentative_name.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
576
577 let i = 0;
578 while (conflict_scopes.find(s => s.conflicting_def_shallow(symbol_name))) {
579 symbol_name = tentative_name + "$" + i++;
580 }
581 }
582
583 if (!symbol_name) {
584 throw new Error("No symbol name could be generated in create_symbol()");
585 }
586
587 const symbol = make_node(SymClass, source, {
588 name: symbol_name,
589 scope
590 });
591
592 this.def_variable(symbol, init || null);
593
594 symbol.mark_enclosed();
595
596 return symbol;
597});
598
599
600AST_Node.DEFMETHOD("is_block_scope", return_false);
601AST_Class.DEFMETHOD("is_block_scope", return_false);
602AST_Lambda.DEFMETHOD("is_block_scope", return_false);
603AST_Toplevel.DEFMETHOD("is_block_scope", return_false);
604AST_SwitchBranch.DEFMETHOD("is_block_scope", return_false);
605AST_Block.DEFMETHOD("is_block_scope", return_true);
606AST_Scope.DEFMETHOD("is_block_scope", function () {
607 return this._block_scope || false;
608});
609AST_IterationStatement.DEFMETHOD("is_block_scope", return_true);
610
611AST_Lambda.DEFMETHOD("init_scope_vars", function() {
612 AST_Scope.prototype.init_scope_vars.apply(this, arguments);
613 this.uses_arguments = false;
614 this.def_variable(new AST_SymbolFunarg({
615 name: "arguments",
616 start: this.start,
617 end: this.end
618 }));
619});
620
621AST_Arrow.DEFMETHOD("init_scope_vars", function() {
622 AST_Scope.prototype.init_scope_vars.apply(this, arguments);
623 this.uses_arguments = false;
624});
625
626AST_Symbol.DEFMETHOD("mark_enclosed", function() {
627 var def = this.definition();
628 var s = this.scope;
629 while (s) {
630 push_uniq(s.enclosed, def);
631 if (s === def.scope) break;
632 s = s.parent_scope;
633 }
634});
635
636AST_Symbol.DEFMETHOD("reference", function() {
637 this.definition().references.push(this);
638 this.mark_enclosed();
639});
640
641AST_Scope.DEFMETHOD("find_variable", function(name) {
642 if (name instanceof AST_Symbol) name = name.name;
643 return this.variables.get(name)
644 || (this.parent_scope && this.parent_scope.find_variable(name));
645});
646
647AST_Scope.DEFMETHOD("def_function", function(symbol, init) {
648 var def = this.def_variable(symbol, init);
649 if (!def.init || def.init instanceof AST_Defun) def.init = init;
650 return def;
651});
652
653AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
654 var def = this.variables.get(symbol.name);
655 if (def) {
656 def.orig.push(symbol);
657 if (def.init && (def.scope !== symbol.scope || def.init instanceof AST_Function)) {
658 def.init = init;
659 }
660 } else {
661 def = new SymbolDef(this, symbol, init);
662 this.variables.set(symbol.name, def);
663 def.global = !this.parent_scope;
664 }
665 return symbol.thedef = def;
666});
667
668function next_mangled(scope, options) {
669 var ext = scope.enclosed;
670 out: while (true) {
671 var m = base54(++scope.cname);
672 if (RESERVED_WORDS.has(m)) continue; // skip over "do"
673
674 // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
675 // shadow a name reserved from mangling.
676 if (options.reserved.has(m)) continue;
677
678 // Functions with short names might collide with base54 output
679 // and therefore cause collisions when keep_fnames is true.
680 if (unmangleable_names && unmangleable_names.has(m)) continue out;
681
682 // we must ensure that the mangled name does not shadow a name
683 // from some parent scope that is referenced in this or in
684 // inner scopes.
685 for (let i = ext.length; --i >= 0;) {
686 const def = ext[i];
687 const name = def.mangled_name || (def.unmangleable(options) && def.name);
688 if (m == name) continue out;
689 }
690 return m;
691 }
692}
693
694AST_Scope.DEFMETHOD("next_mangled", function(options) {
695 return next_mangled(this, options);
696});
697
698AST_Toplevel.DEFMETHOD("next_mangled", function(options) {
699 let name;
700 const mangled_names = this.mangled_names;
701 do {
702 name = next_mangled(this, options);
703 } while (mangled_names.has(name));
704 return name;
705});
706
707AST_Function.DEFMETHOD("next_mangled", function(options, def) {
708 // #179, #326
709 // in Safari strict mode, something like (function x(x){...}) is a syntax error;
710 // a function expression's argument cannot shadow the function expression's name
711
712 var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
713
714 // the function's mangled_name is null when keep_fnames is true
715 var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null;
716
717 while (true) {
718 var name = next_mangled(this, options);
719 if (!tricky_name || tricky_name != name)
720 return name;
721 }
722});
723
724AST_Symbol.DEFMETHOD("unmangleable", function(options) {
725 var def = this.definition();
726 return !def || def.unmangleable(options);
727});
728
729// labels are always mangleable
730AST_Label.DEFMETHOD("unmangleable", return_false);
731
732AST_Symbol.DEFMETHOD("unreferenced", function() {
733 return !this.definition().references.length && !this.scope.pinned();
734});
735
736AST_Symbol.DEFMETHOD("definition", function() {
737 return this.thedef;
738});
739
740AST_Symbol.DEFMETHOD("global", function() {
741 return this.thedef.global;
742});
743
744AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
745 options = defaults(options, {
746 eval : false,
747 ie8 : false,
748 keep_classnames: false,
749 keep_fnames : false,
750 module : false,
751 reserved : [],
752 toplevel : false,
753 });
754 if (options.module) options.toplevel = true;
755 if (!Array.isArray(options.reserved)
756 && !(options.reserved instanceof Set)
757 ) {
758 options.reserved = [];
759 }
760 options.reserved = new Set(options.reserved);
761 // Never mangle arguments
762 options.reserved.add("arguments");
763 return options;
764});
765
766AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
767 options = this._default_mangler_options(options);
768
769 // We only need to mangle declaration nodes. Special logic wired
770 // into the code generator will display the mangled name if it's
771 // present (and for AST_SymbolRef-s it'll use the mangled name of
772 // the AST_SymbolDeclaration that it points to).
773 var lname = -1;
774 var to_mangle = [];
775
776 if (options.keep_fnames) {
777 function_defs = new Set();
778 }
779
780 const mangled_names = this.mangled_names = new Set();
781 if (options.cache) {
782 this.globals.forEach(collect);
783 if (options.cache.props) {
784 options.cache.props.forEach(function(mangled_name) {
785 mangled_names.add(mangled_name);
786 });
787 }
788 }
789
790 var tw = new TreeWalker(function(node, descend) {
791 if (node instanceof AST_LabeledStatement) {
792 // lname is incremented when we get to the AST_Label
793 var save_nesting = lname;
794 descend();
795 lname = save_nesting;
796 return true; // don't descend again in TreeWalker
797 }
798 if (node instanceof AST_Scope) {
799 node.variables.forEach(collect);
800 return;
801 }
802 if (node.is_block_scope()) {
803 node.block_scope.variables.forEach(collect);
804 return;
805 }
806 if (
807 function_defs
808 && node instanceof AST_VarDef
809 && node.value instanceof AST_Lambda
810 && !node.value.name
811 && keep_name(options.keep_fnames, node.name.name)
812 ) {
813 function_defs.add(node.name.definition().id);
814 return;
815 }
816 if (node instanceof AST_Label) {
817 let name;
818 do {
819 name = base54(++lname);
820 } while (RESERVED_WORDS.has(name));
821 node.mangled_name = name;
822 return true;
823 }
824 if (!(options.ie8 || options.safari10) && node instanceof AST_SymbolCatch) {
825 to_mangle.push(node.definition());
826 return;
827 }
828 });
829
830 this.walk(tw);
831
832 if (options.keep_fnames || options.keep_classnames) {
833 unmangleable_names = new Set();
834 // Collect a set of short names which are unmangleable,
835 // for use in avoiding collisions in next_mangled.
836 to_mangle.forEach(def => {
837 if (def.name.length < 6 && def.unmangleable(options)) {
838 unmangleable_names.add(def.name);
839 }
840 });
841 }
842
843 to_mangle.forEach(def => { def.mangle(options); });
844
845 function_defs = null;
846 unmangleable_names = null;
847
848 function collect(symbol) {
849 const should_mangle = !options.reserved.has(symbol.name)
850 && !(symbol.export & MASK_EXPORT_DONT_MANGLE);
851 if (should_mangle) {
852 to_mangle.push(symbol);
853 }
854 }
855});
856
857AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
858 const cache = options.cache && options.cache.props;
859 const avoid = new Set();
860 options.reserved.forEach(to_avoid);
861 this.globals.forEach(add_def);
862 this.walk(new TreeWalker(function(node) {
863 if (node instanceof AST_Scope) node.variables.forEach(add_def);
864 if (node instanceof AST_SymbolCatch) add_def(node.definition());
865 }));
866 return avoid;
867
868 function to_avoid(name) {
869 avoid.add(name);
870 }
871
872 function add_def(def) {
873 var name = def.name;
874 if (def.global && cache && cache.has(name)) name = cache.get(name);
875 else if (!def.unmangleable(options)) return;
876 to_avoid(name);
877 }
878});
879
880AST_Toplevel.DEFMETHOD("expand_names", function(options) {
881 base54.reset();
882 base54.sort();
883 options = this._default_mangler_options(options);
884 var avoid = this.find_colliding_names(options);
885 var cname = 0;
886 this.globals.forEach(rename);
887 this.walk(new TreeWalker(function(node) {
888 if (node instanceof AST_Scope) node.variables.forEach(rename);
889 if (node instanceof AST_SymbolCatch) rename(node.definition());
890 }));
891
892 function next_name() {
893 var name;
894 do {
895 name = base54(cname++);
896 } while (avoid.has(name) || RESERVED_WORDS.has(name));
897 return name;
898 }
899
900 function rename(def) {
901 if (def.global && options.cache) return;
902 if (def.unmangleable(options)) return;
903 if (options.reserved.has(def.name)) return;
904 const redefinition = redefined_catch_def(def);
905 const name = def.name = redefinition ? redefinition.name : next_name();
906 def.orig.forEach(function(sym) {
907 sym.name = name;
908 });
909 def.references.forEach(function(sym) {
910 sym.name = name;
911 });
912 }
913});
914
915AST_Node.DEFMETHOD("tail_node", return_this);
916AST_Sequence.DEFMETHOD("tail_node", function() {
917 return this.expressions[this.expressions.length - 1];
918});
919
920AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
921 options = this._default_mangler_options(options);
922 try {
923 AST_Node.prototype.print = function(stream, force_parens) {
924 this._print(stream, force_parens);
925 if (this instanceof AST_Symbol && !this.unmangleable(options)) {
926 base54.consider(this.name, -1);
927 } else if (options.properties) {
928 if (this instanceof AST_DotHash) {
929 base54.consider("#" + this.property, -1);
930 } else if (this instanceof AST_Dot) {
931 base54.consider(this.property, -1);
932 } else if (this instanceof AST_Sub) {
933 skip_string(this.property);
934 }
935 }
936 };
937 base54.consider(this.print_to_string(), 1);
938 } finally {
939 AST_Node.prototype.print = AST_Node.prototype._print;
940 }
941 base54.sort();
942
943 function skip_string(node) {
944 if (node instanceof AST_String) {
945 base54.consider(node.value, -1);
946 } else if (node instanceof AST_Conditional) {
947 skip_string(node.consequent);
948 skip_string(node.alternative);
949 } else if (node instanceof AST_Sequence) {
950 skip_string(node.tail_node());
951 }
952 }
953});
954
955const base54 = (() => {
956 const leading = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_".split("");
957 const digits = "0123456789".split("");
958 let chars;
959 let frequency;
960 function reset() {
961 frequency = new Map();
962 leading.forEach(function(ch) {
963 frequency.set(ch, 0);
964 });
965 digits.forEach(function(ch) {
966 frequency.set(ch, 0);
967 });
968 }
969 base54.consider = function(str, delta) {
970 for (var i = str.length; --i >= 0;) {
971 frequency.set(str[i], frequency.get(str[i]) + delta);
972 }
973 };
974 function compare(a, b) {
975 return frequency.get(b) - frequency.get(a);
976 }
977 base54.sort = function() {
978 chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
979 };
980 base54.reset = reset;
981 reset();
982 function base54(num) {
983 var ret = "", base = 54;
984 num++;
985 do {
986 num--;
987 ret += chars[num % base];
988 num = Math.floor(num / base);
989 base = 64;
990 } while (num > 0);
991 return ret;
992 }
993 return base54;
994})();
995
996export {
997 base54,
998 SymbolDef,
999};
Note: See TracBrowser for help on using the repository browser.