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