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 | function SymbolDef(scope, orig, init) {
|
47 | this.name = orig.name;
|
48 | this.orig = [ orig ];
|
49 | this.init = init;
|
50 | this.eliminated = 0;
|
51 | this.scope = scope;
|
52 | this.references = [];
|
53 | this.replaced = 0;
|
54 | this.global = false;
|
55 | this.mangled_name = null;
|
56 | this.undeclared = false;
|
57 | this.id = SymbolDef.next_id++;
|
58 | this.lambda = orig instanceof AST_SymbolLambda;
|
59 | }
|
60 |
|
61 | SymbolDef.next_id = 1;
|
62 |
|
63 | SymbolDef.prototype = {
|
64 | unmangleable: function(options) {
|
65 | return this.global && !options.toplevel
|
66 | || this.undeclared
|
67 | || !options.eval && this.scope.pinned()
|
68 | || options.keep_fnames
|
69 | && (this.orig[0] instanceof AST_SymbolLambda
|
70 | || this.orig[0] instanceof AST_SymbolDefun);
|
71 | },
|
72 | mangle: function(options) {
|
73 | var cache = options.cache && options.cache.props;
|
74 | if (this.global && cache && cache.has(this.name)) {
|
75 | this.mangled_name = cache.get(this.name);
|
76 | } else if (!this.mangled_name && !this.unmangleable(options)) {
|
77 | var def;
|
78 | if (def = this.redefined()) {
|
79 | this.mangled_name = def.mangled_name || def.name;
|
80 | } else {
|
81 | this.mangled_name = next_mangled_name(this.scope, options, this);
|
82 | }
|
83 | if (this.global && cache) {
|
84 | cache.set(this.name, this.mangled_name);
|
85 | }
|
86 | }
|
87 | },
|
88 | redefined: function() {
|
89 | return this.defun && this.defun.variables.get(this.name);
|
90 | }
|
91 | };
|
92 |
|
93 | AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
94 | options = defaults(options, {
|
95 | cache: null,
|
96 | ie8: false,
|
97 | });
|
98 |
|
99 |
|
100 | var self = this;
|
101 | var scope = self.parent_scope = null;
|
102 | var defun = null;
|
103 | var tw = new TreeWalker(function(node, descend) {
|
104 | if (node instanceof AST_Catch) {
|
105 | var save_scope = scope;
|
106 | scope = new AST_Scope(node);
|
107 | scope.init_scope_vars(save_scope);
|
108 | descend();
|
109 | scope = save_scope;
|
110 | return true;
|
111 | }
|
112 | if (node instanceof AST_Scope) {
|
113 | node.init_scope_vars(scope);
|
114 | var save_scope = scope;
|
115 | var save_defun = defun;
|
116 | defun = scope = node;
|
117 | descend();
|
118 | scope = save_scope;
|
119 | defun = save_defun;
|
120 | return true;
|
121 | }
|
122 | if (node instanceof AST_With) {
|
123 | for (var s = scope; s; s = s.parent_scope) s.uses_with = true;
|
124 | return;
|
125 | }
|
126 | if (node instanceof AST_Symbol) {
|
127 | node.scope = scope;
|
128 | }
|
129 | if (node instanceof AST_Label) {
|
130 | node.thedef = node;
|
131 | node.references = [];
|
132 | }
|
133 | if (node instanceof AST_SymbolDefun) {
|
134 |
|
135 |
|
136 | (node.scope = defun.parent_scope.resolve()).def_function(node, defun);
|
137 | } else if (node instanceof AST_SymbolLambda) {
|
138 | var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
|
139 | if (options.ie8) def.defun = defun.parent_scope.resolve();
|
140 | } else if (node instanceof AST_SymbolVar) {
|
141 | defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
|
142 | if (defun !== scope) {
|
143 | node.mark_enclosed(options);
|
144 | var def = scope.find_variable(node);
|
145 | if (node.thedef !== def) {
|
146 | node.thedef = def;
|
147 | }
|
148 | node.reference(options);
|
149 | }
|
150 | } else if (node instanceof AST_SymbolCatch) {
|
151 | scope.def_variable(node).defun = defun;
|
152 | }
|
153 | });
|
154 | self.walk(tw);
|
155 |
|
156 |
|
157 | self.globals = new Dictionary();
|
158 | var tw = new TreeWalker(function(node) {
|
159 | if (node instanceof AST_LoopControl) {
|
160 | if (node.label) node.label.thedef.references.push(node);
|
161 | return true;
|
162 | }
|
163 | if (node instanceof AST_SymbolRef) {
|
164 | var name = node.name;
|
165 | var sym = node.scope.find_variable(name);
|
166 | if (!sym) {
|
167 | sym = self.def_global(node);
|
168 | } else if (sym.scope instanceof AST_Lambda && name == "arguments") {
|
169 | sym.scope.uses_arguments = true;
|
170 | }
|
171 | if (name == "eval") {
|
172 | var parent = tw.parent();
|
173 | if (parent.TYPE == "Call" && parent.expression === node) {
|
174 | for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
|
175 | s.uses_eval = true;
|
176 | }
|
177 | } else if (sym.undeclared) {
|
178 | self.uses_eval = true;
|
179 | }
|
180 | }
|
181 | node.thedef = sym;
|
182 | node.reference(options);
|
183 | return true;
|
184 | }
|
185 |
|
186 | if (node instanceof AST_SymbolCatch) {
|
187 | var def = node.definition().redefined();
|
188 | if (def) for (var s = node.scope; s; s = s.parent_scope) {
|
189 | push_uniq(s.enclosed, def);
|
190 | if (s === def.scope) break;
|
191 | }
|
192 | return true;
|
193 | }
|
194 | });
|
195 | self.walk(tw);
|
196 |
|
197 |
|
198 | if (options.ie8) self.walk(new TreeWalker(function(node) {
|
199 | if (node instanceof AST_SymbolCatch) {
|
200 | var scope = node.thedef.defun;
|
201 | if (scope.name instanceof AST_SymbolLambda && scope.name.name == node.name) {
|
202 | scope = scope.parent_scope.resolve();
|
203 | }
|
204 | redefine(node, scope);
|
205 | return true;
|
206 | }
|
207 | if (node instanceof AST_SymbolLambda) {
|
208 | var def = node.thedef;
|
209 | redefine(node, node.scope.parent_scope.resolve());
|
210 | if (typeof node.thedef.init !== "undefined") {
|
211 | node.thedef.init = false;
|
212 | } else if (def.init) {
|
213 | node.thedef.init = def.init;
|
214 | }
|
215 | return true;
|
216 | }
|
217 | }));
|
218 |
|
219 | function redefine(node, scope) {
|
220 | var name = node.name;
|
221 | var old_def = node.thedef;
|
222 | var new_def = scope.find_variable(name);
|
223 | if (new_def) {
|
224 | var redef;
|
225 | while (redef = new_def.redefined()) new_def = redef;
|
226 | } else {
|
227 | new_def = self.globals.get(name);
|
228 | }
|
229 | if (new_def) {
|
230 | new_def.orig.push(node);
|
231 | } else {
|
232 | new_def = scope.def_variable(node);
|
233 | }
|
234 | old_def.orig.concat(old_def.references).forEach(function(node) {
|
235 | node.thedef = new_def;
|
236 | node.reference(options);
|
237 | });
|
238 | if (old_def.lambda) new_def.lambda = true;
|
239 | if (new_def.undeclared) self.variables.set(name, new_def);
|
240 | }
|
241 | });
|
242 |
|
243 | AST_Toplevel.DEFMETHOD("def_global", function(node) {
|
244 | var globals = this.globals, name = node.name;
|
245 | if (globals.has(name)) {
|
246 | return globals.get(name);
|
247 | } else {
|
248 | var g = new SymbolDef(this, node);
|
249 | g.undeclared = true;
|
250 | g.global = true;
|
251 | globals.set(name, g);
|
252 | return g;
|
253 | }
|
254 | });
|
255 |
|
256 | AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
|
257 | this.variables = new Dictionary();
|
258 | this.functions = new Dictionary();
|
259 | this.uses_with = false;
|
260 | this.uses_eval = false;
|
261 | this.parent_scope = parent_scope;
|
262 | this.enclosed = [];
|
263 | this.cname = -1;
|
264 | });
|
265 |
|
266 | AST_Lambda.DEFMETHOD("init_scope_vars", function() {
|
267 | AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
268 | this.uses_arguments = false;
|
269 | this.def_variable(new AST_SymbolFunarg({
|
270 | name: "arguments",
|
271 | start: this.start,
|
272 | end: this.end
|
273 | }));
|
274 | });
|
275 |
|
276 | AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
|
277 | var def = this.definition();
|
278 | for (var s = this.scope; s; s = s.parent_scope) {
|
279 | push_uniq(s.enclosed, def);
|
280 | if (options.keep_fnames) {
|
281 | s.functions.each(function(d) {
|
282 | push_uniq(def.scope.enclosed, d);
|
283 | });
|
284 | }
|
285 | if (s === def.scope) break;
|
286 | }
|
287 | });
|
288 |
|
289 | AST_Symbol.DEFMETHOD("reference", function(options) {
|
290 | this.definition().references.push(this);
|
291 | this.mark_enclosed(options);
|
292 | });
|
293 |
|
294 | AST_Scope.DEFMETHOD("find_variable", function(name) {
|
295 | if (name instanceof AST_Symbol) name = name.name;
|
296 | return this.variables.get(name)
|
297 | || (this.parent_scope && this.parent_scope.find_variable(name));
|
298 | });
|
299 |
|
300 | AST_Scope.DEFMETHOD("def_function", function(symbol, init) {
|
301 | var def = this.def_variable(symbol, init);
|
302 | if (!def.init || def.init instanceof AST_Defun) def.init = init;
|
303 | this.functions.set(symbol.name, def);
|
304 | return def;
|
305 | });
|
306 |
|
307 | AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
|
308 | var def = this.variables.get(symbol.name);
|
309 | if (def) {
|
310 | def.orig.push(symbol);
|
311 | if (def.init instanceof AST_Function) def.init = init;
|
312 | } else {
|
313 | def = new SymbolDef(this, symbol, init);
|
314 | this.variables.set(symbol.name, def);
|
315 | def.global = !this.parent_scope;
|
316 | }
|
317 | return symbol.thedef = def;
|
318 | });
|
319 |
|
320 | AST_Lambda.DEFMETHOD("resolve", return_this);
|
321 | AST_Scope.DEFMETHOD("resolve", function() {
|
322 | return this.parent_scope.resolve();
|
323 | });
|
324 | AST_Toplevel.DEFMETHOD("resolve", return_this);
|
325 |
|
326 | function names_in_use(scope, options) {
|
327 | var names = scope.names_in_use;
|
328 | if (!names) {
|
329 | scope.names_in_use = names = Object.create(scope.mangled_names || null);
|
330 | scope.cname_holes = [];
|
331 | var cache = options.cache && options.cache.props;
|
332 | scope.enclosed.forEach(function(def) {
|
333 | if (def.unmangleable(options)) names[def.name] = true;
|
334 | if (def.global && cache && cache.has(def.name)) {
|
335 | names[cache.get(def.name)] = true;
|
336 | }
|
337 | });
|
338 | }
|
339 | return names;
|
340 | }
|
341 |
|
342 | function next_mangled_name(scope, options, def) {
|
343 | var in_use = names_in_use(scope, options);
|
344 | var holes = scope.cname_holes;
|
345 | var names = Object.create(null);
|
346 | var scopes = [ scope ];
|
347 | def.references.forEach(function(sym) {
|
348 | var scope = sym.scope;
|
349 | do {
|
350 | if (scopes.indexOf(scope) < 0) {
|
351 | for (var name in names_in_use(scope, options)) {
|
352 | names[name] = true;
|
353 | }
|
354 | scopes.push(scope);
|
355 | } else break;
|
356 | } while (scope = scope.parent_scope);
|
357 | });
|
358 | var name;
|
359 | for (var i = 0; i < holes.length; i++) {
|
360 | name = base54(holes[i]);
|
361 | if (names[name]) continue;
|
362 | holes.splice(i, 1);
|
363 | scope.names_in_use[name] = true;
|
364 | return name;
|
365 | }
|
366 | while (true) {
|
367 | name = base54(++scope.cname);
|
368 | if (in_use[name] || RESERVED_WORDS[name] || options.reserved.has[name]) continue;
|
369 | if (!names[name]) break;
|
370 | holes.push(scope.cname);
|
371 | }
|
372 | scope.names_in_use[name] = true;
|
373 | return name;
|
374 | }
|
375 |
|
376 | AST_Symbol.DEFMETHOD("unmangleable", function(options) {
|
377 | var def = this.definition();
|
378 | return !def || def.unmangleable(options);
|
379 | });
|
380 |
|
381 |
|
382 | AST_Label.DEFMETHOD("unmangleable", return_false);
|
383 |
|
384 | AST_Symbol.DEFMETHOD("unreferenced", function() {
|
385 | return !this.definition().references.length && !this.scope.pinned();
|
386 | });
|
387 |
|
388 | AST_Symbol.DEFMETHOD("definition", function() {
|
389 | return this.thedef;
|
390 | });
|
391 |
|
392 | AST_Symbol.DEFMETHOD("global", function() {
|
393 | return this.definition().global;
|
394 | });
|
395 |
|
396 | function _default_mangler_options(options) {
|
397 | options = defaults(options, {
|
398 | eval : false,
|
399 | ie8 : false,
|
400 | keep_fnames : false,
|
401 | reserved : [],
|
402 | toplevel : false,
|
403 | });
|
404 | if (!Array.isArray(options.reserved)) options.reserved = [];
|
405 |
|
406 | push_uniq(options.reserved, "arguments");
|
407 | options.reserved.has = makePredicate(options.reserved);
|
408 | return options;
|
409 | }
|
410 |
|
411 | AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
|
412 | options = _default_mangler_options(options);
|
413 |
|
414 |
|
415 |
|
416 |
|
417 |
|
418 | var lname = -1;
|
419 |
|
420 | if (options.cache && options.cache.props) {
|
421 | var mangled_names = this.mangled_names = Object.create(null);
|
422 | options.cache.props.each(function(mangled_name) {
|
423 | mangled_names[mangled_name] = true;
|
424 | });
|
425 | }
|
426 |
|
427 | var redefined = [];
|
428 | var tw = new TreeWalker(function(node, descend) {
|
429 | if (node instanceof AST_LabeledStatement) {
|
430 |
|
431 | var save_nesting = lname;
|
432 | descend();
|
433 | lname = save_nesting;
|
434 | return true;
|
435 | }
|
436 | if (node instanceof AST_Scope) {
|
437 | descend();
|
438 | if (options.cache && node instanceof AST_Toplevel) {
|
439 | node.globals.each(mangle);
|
440 | }
|
441 | if (node instanceof AST_Defun && tw.has_directive("use asm")) {
|
442 | var sym = new AST_SymbolRef(node.name);
|
443 | sym.scope = node;
|
444 | sym.reference(options);
|
445 | }
|
446 | node.variables.each(function(def) {
|
447 | if (!defer_redef(def)) mangle(def);
|
448 | });
|
449 | return true;
|
450 | }
|
451 | if (node instanceof AST_Label) {
|
452 | var name;
|
453 | do {
|
454 | name = base54(++lname);
|
455 | } while (RESERVED_WORDS[name]);
|
456 | node.mangled_name = name;
|
457 | return true;
|
458 | }
|
459 | if (!options.ie8 && node instanceof AST_Catch) {
|
460 | var def = node.argname.definition();
|
461 | var redef = defer_redef(def, node.argname);
|
462 | descend();
|
463 | if (!redef) mangle(def);
|
464 | return true;
|
465 | }
|
466 | });
|
467 | this.walk(tw);
|
468 | redefined.forEach(mangle);
|
469 |
|
470 | function mangle(def) {
|
471 | if (options.reserved.has[def.name]) return;
|
472 | def.mangle(options);
|
473 | }
|
474 |
|
475 | function defer_redef(def, node) {
|
476 | var redef = def.redefined();
|
477 | if (!redef) return false;
|
478 | redefined.push(def);
|
479 | def.references.forEach(reference);
|
480 | if (node) reference(node);
|
481 | return true;
|
482 |
|
483 | function reference(sym) {
|
484 | sym.thedef = redef;
|
485 | sym.reference(options);
|
486 | sym.thedef = def;
|
487 | }
|
488 | }
|
489 | });
|
490 |
|
491 | AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
|
492 | var cache = options.cache && options.cache.props;
|
493 | var avoid = Object.create(null);
|
494 | options.reserved.forEach(to_avoid);
|
495 | this.globals.each(add_def);
|
496 | this.walk(new TreeWalker(function(node) {
|
497 | if (node instanceof AST_Scope) node.variables.each(add_def);
|
498 | if (node instanceof AST_SymbolCatch) add_def(node.definition());
|
499 | }));
|
500 | return avoid;
|
501 |
|
502 | function to_avoid(name) {
|
503 | avoid[name] = true;
|
504 | }
|
505 |
|
506 | function add_def(def) {
|
507 | var name = def.name;
|
508 | if (def.global && cache && cache.has(name)) name = cache.get(name);
|
509 | else if (!def.unmangleable(options)) return;
|
510 | to_avoid(name);
|
511 | }
|
512 | });
|
513 |
|
514 | AST_Toplevel.DEFMETHOD("expand_names", function(options) {
|
515 | base54.reset();
|
516 | base54.sort();
|
517 | options = _default_mangler_options(options);
|
518 | var avoid = this.find_colliding_names(options);
|
519 | var cname = 0;
|
520 | this.globals.each(rename);
|
521 | this.walk(new TreeWalker(function(node) {
|
522 | if (node instanceof AST_Scope) node.variables.each(rename);
|
523 | if (node instanceof AST_SymbolCatch) rename(node.definition());
|
524 | }));
|
525 |
|
526 | function next_name() {
|
527 | var name;
|
528 | do {
|
529 | name = base54(cname++);
|
530 | } while (avoid[name] || RESERVED_WORDS[name]);
|
531 | return name;
|
532 | }
|
533 |
|
534 | function rename(def) {
|
535 | if (def.global && options.cache) return;
|
536 | if (def.unmangleable(options)) return;
|
537 | if (options.reserved.has[def.name]) return;
|
538 | var redef = def.redefined();
|
539 | var name = redef ? redef.rename || redef.name : next_name();
|
540 | def.rename = name;
|
541 | def.orig.forEach(function(sym) {
|
542 | sym.name = name;
|
543 | });
|
544 | def.references.forEach(function(sym) {
|
545 | sym.name = name;
|
546 | });
|
547 | }
|
548 | });
|
549 |
|
550 | AST_Node.DEFMETHOD("tail_node", return_this);
|
551 | AST_Sequence.DEFMETHOD("tail_node", function() {
|
552 | return this.expressions[this.expressions.length - 1];
|
553 | });
|
554 |
|
555 | AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
|
556 | options = _default_mangler_options(options);
|
557 | base54.reset();
|
558 | try {
|
559 | AST_Node.prototype.print = function(stream, force_parens) {
|
560 | this._print(stream, force_parens);
|
561 | if (this instanceof AST_Symbol && !this.unmangleable(options)) {
|
562 | base54.consider(this.name, -1);
|
563 | } else if (options.properties) {
|
564 | if (this instanceof AST_Dot) {
|
565 | base54.consider(this.property, -1);
|
566 | } else if (this instanceof AST_Sub) {
|
567 | skip_string(this.property);
|
568 | }
|
569 | }
|
570 | };
|
571 | base54.consider(this.print_to_string(), 1);
|
572 | } finally {
|
573 | AST_Node.prototype.print = AST_Node.prototype._print;
|
574 | }
|
575 | base54.sort();
|
576 |
|
577 | function skip_string(node) {
|
578 | if (node instanceof AST_String) {
|
579 | base54.consider(node.value, -1);
|
580 | } else if (node instanceof AST_Conditional) {
|
581 | skip_string(node.consequent);
|
582 | skip_string(node.alternative);
|
583 | } else if (node instanceof AST_Sequence) {
|
584 | skip_string(node.tail_node());
|
585 | }
|
586 | }
|
587 | });
|
588 |
|
589 | var base54 = (function() {
|
590 | var freq = Object.create(null);
|
591 | function init(chars) {
|
592 | var array = [];
|
593 | for (var i = 0; i < chars.length; i++) {
|
594 | var ch = chars[i];
|
595 | array.push(ch);
|
596 | freq[ch] = -1e-2 * i;
|
597 | }
|
598 | return array;
|
599 | }
|
600 | var digits = init("0123456789");
|
601 | var leading = init("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_");
|
602 | var chars, frequency;
|
603 | function reset() {
|
604 | frequency = Object.create(freq);
|
605 | }
|
606 | base54.consider = function(str, delta) {
|
607 | for (var i = str.length; --i >= 0;) {
|
608 | frequency[str[i]] += delta;
|
609 | }
|
610 | };
|
611 | function compare(a, b) {
|
612 | return frequency[b] - frequency[a];
|
613 | }
|
614 | base54.sort = function() {
|
615 | chars = leading.sort(compare).concat(digits.sort(compare));
|
616 | };
|
617 | base54.reset = reset;
|
618 | reset();
|
619 | function base54(num) {
|
620 | var ret = "", base = 54;
|
621 | num++;
|
622 | do {
|
623 | num--;
|
624 | ret += chars[num % base];
|
625 | num = Math.floor(num / base);
|
626 | base = 64;
|
627 | } while (num > 0);
|
628 | return ret;
|
629 | }
|
630 | return base54;
|
631 | })();
|