1/*********************************************************************** 2 3 A JavaScript tokenizer / parser / beautifier / compressor. 4 https://github.com/mishoo/UglifyJS 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 46function SymbolDef(id, scope, orig, init) { 47 this._bits = 0; 48 this.defun = undefined; 49 this.eliminated = 0; 50 this.id = id; 51 this.init = init; 52 this.mangled_name = null; 53 this.name = orig.name; 54 this.orig = [ orig ]; 55 this.references = []; 56 this.replaced = 0; 57 this.safe_ids = undefined; 58 this.scope = scope; 59} 60 61SymbolDef.prototype = { 62 forEach: function(fn) { 63 this.orig.forEach(fn); 64 this.references.forEach(fn); 65 }, 66 mangle: function(options) { 67 if (this.mangled_name) return; 68 var cache = this.global && options.cache && options.cache.props; 69 if (cache && cache.has(this.name)) { 70 this.mangled_name = cache.get(this.name); 71 } else if (this.unmangleable(options)) { 72 names_in_use(this.scope, options).set(this.name, true); 73 } else { 74 var def = this.redefined(); 75 if (def) { 76 this.mangled_name = def.mangled_name || def.name; 77 } else { 78 this.mangled_name = next_mangled_name(this, options); 79 } 80 if (cache) cache.set(this.name, this.mangled_name); 81 } 82 }, 83 redefined: function() { 84 var self = this; 85 var scope = self.defun; 86 if (!scope) return; 87 var name = self.name; 88 var def = scope.variables.get(name) 89 || scope instanceof AST_Toplevel && scope.globals.get(name) 90 || self.orig[0] instanceof AST_SymbolConst && find_if(function(def) { 91 return def.name == name; 92 }, scope.enclosed); 93 if (def && def !== self) return def.redefined() || def; 94 }, 95 unmangleable: function(options) { 96 if (this.exported) return true; 97 if (this.undeclared) return true; 98 if (!options.eval && this.scope.pinned()) return true; 99 if (options.keep_fargs && is_funarg(this)) return true; 100 if (options.keep_fnames) { 101 var sym = this.orig[0]; 102 if (sym instanceof AST_SymbolClass) return true; 103 if (sym instanceof AST_SymbolDefClass) return true; 104 if (sym instanceof AST_SymbolDefun) return true; 105 if (sym instanceof AST_SymbolLambda) return true; 106 } 107 if (!options.toplevel && this.global) return true; 108 return false; 109 }, 110}; 111 112DEF_BITPROPS(SymbolDef, [ 113 "const_redefs", 114 "cross_loop", 115 "direct_access", 116 "exported", 117 "global", 118 "undeclared", 119]); 120 121function is_funarg(def) { 122 return def.orig[0] instanceof AST_SymbolFunarg || def.orig[1] instanceof AST_SymbolFunarg; 123} 124 125var unary_side_effects = makePredicate("delete ++ --"); 126 127function is_lhs(node, parent) { 128 if (parent instanceof AST_Assign) return parent.left === node && node; 129 if (parent instanceof AST_DefaultValue) return parent.name === node && node; 130 if (parent instanceof AST_Destructured) return node; 131 if (parent instanceof AST_DestructuredKeyVal) return node; 132 if (parent instanceof AST_ForEnumeration) return parent.init === node && node; 133 if (parent instanceof AST_Unary) return unary_side_effects[parent.operator] && parent.expression; 134} 135 136AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { 137 options = defaults(options, { 138 cache: null, 139 ie: false, 140 }); 141 142 // pass 1: setup scope chaining and handle definitions 143 var self = this; 144 var defun = null; 145 var exported = false; 146 var next_def_id = 0; 147 var scope = self.parent_scope = null; 148 var tw = new TreeWalker(function(node, descend) { 149 if (node instanceof AST_DefClass) { 150 var save_exported = exported; 151 exported = tw.parent() instanceof AST_ExportDeclaration; 152 node.name.walk(tw); 153 exported = save_exported; 154 walk_scope(function() { 155 if (node.extends) node.extends.walk(tw); 156 node.properties.forEach(function(prop) { 157 prop.walk(tw); 158 }); 159 }); 160 return true; 161 } 162 if (node instanceof AST_Definitions) { 163 var save_exported = exported; 164 exported = tw.parent() instanceof AST_ExportDeclaration; 165 descend(); 166 exported = save_exported; 167 return true; 168 } 169 if (node instanceof AST_LambdaDefinition) { 170 var save_exported = exported; 171 exported = tw.parent() instanceof AST_ExportDeclaration; 172 node.name.walk(tw); 173 exported = save_exported; 174 walk_scope(function() { 175 node.argnames.forEach(function(argname) { 176 argname.walk(tw); 177 }); 178 if (node.rest) node.rest.walk(tw); 179 walk_body(node, tw); 180 }); 181 return true; 182 } 183 if (node instanceof AST_SwitchBranch) { 184 node.init_vars(scope); 185 descend(); 186 return true; 187 } 188 if (node instanceof AST_Try) { 189 walk_scope(function() { 190 walk_body(node, tw); 191 }); 192 if (node.bcatch) node.bcatch.walk(tw); 193 if (node.bfinally) node.bfinally.walk(tw); 194 return true; 195 } 196 if (node instanceof AST_With) { 197 var s = scope; 198 do { 199 s = s.resolve(); 200 if (s.uses_with) break; 201 s.uses_with = true; 202 } while (s = s.parent_scope); 203 walk_scope(descend); 204 return true; 205 } 206 if (node instanceof AST_BlockScope) { 207 walk_scope(descend); 208 return true; 209 } 210 if (node instanceof AST_Symbol) { 211 node.scope = scope; 212 } 213 if (node instanceof AST_Label) { 214 node.thedef = node; 215 node.references = []; 216 } 217 if (node instanceof AST_SymbolCatch) { 218 scope.def_variable(node).defun = defun; 219 } else if (node instanceof AST_SymbolConst) { 220 var def = scope.def_variable(node); 221 def.defun = defun; 222 if (exported) def.exported = true; 223 } else if (node instanceof AST_SymbolDefun) { 224 var def = defun.def_function(node, tw.parent()); 225 if (exported) def.exported = true; 226 } else if (node instanceof AST_SymbolFunarg) { 227 defun.def_variable(node); 228 } else if (node instanceof AST_SymbolLambda) { 229 var def = defun.def_function(node, node.name == "arguments" ? undefined : defun); 230 if (options.ie && node.name != "arguments") def.defun = defun.parent_scope.resolve(); 231 } else if (node instanceof AST_SymbolLet) { 232 var def = scope.def_variable(node); 233 if (exported) def.exported = true; 234 } else if (node instanceof AST_SymbolVar) { 235 var def = defun.def_variable(node, node instanceof AST_SymbolImport ? undefined : null); 236 if (exported) def.exported = true; 237 } 238 239 function walk_scope(descend) { 240 node.init_vars(scope); 241 var save_defun = defun; 242 var save_scope = scope; 243 if (node instanceof AST_Scope) defun = node; 244 scope = node; 245 descend(); 246 scope = save_scope; 247 defun = save_defun; 248 } 249 }); 250 self.make_def = function(orig, init) { 251 return new SymbolDef(++next_def_id, this, orig, init); 252 }; 253 self.walk(tw); 254 255 // pass 2: find back references and eval 256 self.globals = new Dictionary(); 257 var in_arg = []; 258 var tw = new TreeWalker(function(node) { 259 if (node instanceof AST_Catch) { 260 if (!(node.argname instanceof AST_Destructured)) return; 261 in_arg.push(node); 262 node.argname.walk(tw); 263 in_arg.pop(); 264 walk_body(node, tw); 265 return true; 266 } 267 if (node instanceof AST_Lambda) { 268 in_arg.push(node); 269 if (node.name) node.name.walk(tw); 270 node.argnames.forEach(function(argname) { 271 argname.walk(tw); 272 }); 273 if (node.rest) node.rest.walk(tw); 274 in_arg.pop(); 275 walk_lambda(node, tw); 276 return true; 277 } 278 if (node instanceof AST_LoopControl) { 279 if (node.label) node.label.thedef.references.push(node); 280 return true; 281 } 282 if (node instanceof AST_SymbolDeclaration) { 283 var def = node.definition(); 284 def.preinit = def.references.length; 285 if (node instanceof AST_SymbolCatch) { 286 // ensure mangling works if `catch` reuses a scope variable 287 var redef = def.redefined(); 288 if (redef) for (var s = node.scope; s; s = s.parent_scope) { 289 if (!push_uniq(s.enclosed, redef)) break; 290 if (s === redef.scope) break; 291 } 292 } else if (node instanceof AST_SymbolConst) { 293 // ensure compression works if `const` reuses a scope variable 294 var redef = def.redefined(); 295 if (redef) redef.const_redefs = true; 296 } else if (def.scope !== node.scope && (node instanceof AST_SymbolDefun 297 || node instanceof AST_SymbolFunarg 298 || node instanceof AST_SymbolVar)) { 299 node.mark_enclosed(options); 300 var redef = node.scope.find_variable(node.name); 301 if (node.thedef !== redef) { 302 node.thedef = redef; 303 redef.orig.push(node); 304 node.mark_enclosed(options); 305 } 306 } 307 if (node.name != "arguments") return true; 308 var parent = node instanceof AST_SymbolVar && tw.parent(); 309 if (parent instanceof AST_VarDef && !parent.value) return true; 310 var sym = node.scope.resolve().find_variable("arguments"); 311 if (sym && is_arguments(sym)) sym.scope.uses_arguments = 3; 312 return true; 313 } 314 if (node instanceof AST_SymbolRef) { 315 var name = node.name; 316 var sym = node.scope.find_variable(name); 317 for (var i = in_arg.length; i > 0 && sym;) { 318 i = in_arg.lastIndexOf(sym.scope, i - 1); 319 if (i < 0) break; 320 var decl = sym.orig[0]; 321 if (decl instanceof AST_SymbolCatch 322 || decl instanceof AST_SymbolFunarg 323 || decl instanceof AST_SymbolLambda) { 324 node.in_arg = true; 325 break; 326 } 327 sym = sym.scope.parent_scope.find_variable(name); 328 } 329 if (!sym) { 330 sym = self.def_global(node); 331 } else if (name == "arguments" && is_arguments(sym)) { 332 var parent = tw.parent(); 333 if (is_lhs(node, parent)) { 334 sym.scope.uses_arguments = 3; 335 } else if (sym.scope.uses_arguments < 2 336 && !(parent instanceof AST_PropAccess && parent.expression === node)) { 337 sym.scope.uses_arguments = 2; 338 } else if (!sym.scope.uses_arguments) { 339 sym.scope.uses_arguments = true; 340 } 341 } 342 if (name == "eval") { 343 var parent = tw.parent(); 344 if (parent.TYPE == "Call" && parent.expression === node) { 345 var s = node.scope; 346 do { 347 s = s.resolve(); 348 if (s.uses_eval) break; 349 s.uses_eval = true; 350 } while (s = s.parent_scope); 351 } else if (sym.undeclared) { 352 self.uses_eval = true; 353 } 354 } 355 if (sym.init instanceof AST_LambdaDefinition && sym.scope !== sym.init.name.scope) { 356 var scope = node.scope; 357 do { 358 if (scope === sym.init.name.scope) break; 359 } while (scope = scope.parent_scope); 360 if (!scope) sym.init = undefined; 361 } 362 node.thedef = sym; 363 node.reference(options); 364 return true; 365 } 366 }); 367 self.walk(tw); 368 369 // pass 3: fix up any scoping issue with IE8 370 if (options.ie) self.walk(new TreeWalker(function(node) { 371 if (node instanceof AST_SymbolCatch) { 372 var def = node.thedef; 373 var scope = def.defun; 374 if (def.name != "arguments" && scope.name instanceof AST_SymbolLambda && scope.name.name == def.name) { 375 scope = scope.parent_scope.resolve(); 376 } 377 redefine(node, scope); 378 return true; 379 } 380 if (node instanceof AST_SymbolLambda) { 381 var def = node.thedef; 382 if (!redefine(node, node.scope.parent_scope.resolve())) { 383 def.defun = undefined; 384 } else if (typeof node.thedef.init !== "undefined") { 385 node.thedef.init = false; 386 } else if (def.init) { 387 node.thedef.init = def.init; 388 } 389 return true; 390 } 391 })); 392 393 function is_arguments(sym) { 394 return sym.orig[0] instanceof AST_SymbolFunarg 395 && !(sym.orig[1] instanceof AST_SymbolFunarg || sym.orig[2] instanceof AST_SymbolFunarg) 396 && !is_arrow(sym.scope); 397 } 398 399 function redefine(node, scope) { 400 var name = node.name; 401 var old_def = node.thedef; 402 if (!all(old_def.orig, function(sym) { 403 return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet); 404 })) return false; 405 var new_def = scope.find_variable(name); 406 if (new_def) { 407 var redef = new_def.redefined(); 408 if (redef) new_def = redef; 409 } else { 410 new_def = self.globals.get(name); 411 } 412 if (new_def) { 413 new_def.orig.push(node); 414 } else { 415 new_def = scope.def_variable(node); 416 } 417 if (new_def.undeclared) self.variables.set(name, new_def); 418 if (name == "arguments" && is_arguments(old_def) && node instanceof AST_SymbolLambda) return true; 419 old_def.defun = new_def.scope; 420 old_def.forEach(function(node) { 421 node.redef = old_def; 422 node.thedef = new_def; 423 node.reference(options); 424 }); 425 return true; 426 } 427}); 428 429AST_Toplevel.DEFMETHOD("def_global", function(node) { 430 var globals = this.globals, name = node.name; 431 if (globals.has(name)) { 432 return globals.get(name); 433 } else { 434 var g = this.make_def(node); 435 g.undeclared = true; 436 g.global = true; 437 globals.set(name, g); 438 return g; 439 } 440}); 441 442function init_block_vars(scope, parent) { 443 scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes 444 scope.parent_scope = parent; // the parent scope (null if this is the top level) 445 scope.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) 446 scope.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) 447 if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances 448} 449 450function init_scope_vars(scope, parent) { 451 init_block_vars(scope, parent); 452 scope.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` 453 scope.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement 454} 455 456AST_BlockScope.DEFMETHOD("init_vars", function(parent_scope) { 457 init_block_vars(this, parent_scope); 458}); 459AST_Scope.DEFMETHOD("init_vars", function(parent_scope) { 460 init_scope_vars(this, parent_scope); 461}); 462AST_Arrow.DEFMETHOD("init_vars", function(parent_scope) { 463 init_scope_vars(this, parent_scope); 464 return this; 465}); 466AST_AsyncArrow.DEFMETHOD("init_vars", function(parent_scope) { 467 init_scope_vars(this, parent_scope); 468}); 469AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) { 470 init_scope_vars(this, parent_scope); 471 this.uses_arguments = false; 472 this.def_variable(new AST_SymbolFunarg({ 473 name: "arguments", 474 scope: this, 475 start: this.start, 476 end: this.end, 477 })); 478 return this; 479}); 480 481AST_Symbol.DEFMETHOD("mark_enclosed", function(options) { 482 var def = this.definition(); 483 for (var s = this.scope; s; s = s.parent_scope) { 484 if (!push_uniq(s.enclosed, def)) break; 485 if (!options) { 486 s._var_names = undefined; 487 } else { 488 if (options.keep_fargs && s instanceof AST_Lambda) s.each_argname(function(arg) { 489 push_uniq(def.scope.enclosed, arg.definition()); 490 }); 491 if (options.keep_fnames) s.functions.each(function(d) { 492 push_uniq(def.scope.enclosed, d); 493 }); 494 } 495 if (s === def.scope) break; 496 } 497}); 498 499AST_Symbol.DEFMETHOD("reference", function(options) { 500 this.definition().references.push(this); 501 this.mark_enclosed(options); 502}); 503 504AST_BlockScope.DEFMETHOD("find_variable", function(name) { 505 return this.variables.get(name) 506 || this.parent_scope && this.parent_scope.find_variable(name); 507}); 508 509AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) { 510 var def = this.def_variable(symbol, init); 511 if (!def.init || def.init instanceof AST_LambdaDefinition) def.init = init; 512 this.functions.set(symbol.name, def); 513 return def; 514}); 515 516AST_BlockScope.DEFMETHOD("def_variable", function(symbol, init) { 517 var def = this.variables.get(symbol.name); 518 if (def) { 519 def.orig.push(symbol); 520 if (def.init instanceof AST_LambdaExpression) def.init = init; 521 } else { 522 def = this.make_def(symbol, init); 523 this.variables.set(symbol.name, def); 524 def.global = !this.parent_scope; 525 } 526 return symbol.thedef = def; 527}); 528 529function names_in_use(scope, options) { 530 var names = scope.names_in_use; 531 if (!names) { 532 scope.cname = -1; 533 scope.cname_holes = []; 534 scope.names_in_use = names = new Dictionary(); 535 var cache = options.cache && options.cache.props; 536 scope.enclosed.forEach(function(def) { 537 if (def.unmangleable(options)) names.set(def.name, true); 538 if (def.global && cache && cache.has(def.name)) { 539 names.set(cache.get(def.name), true); 540 } 541 }); 542 } 543 return names; 544} 545 546function next_mangled_name(def, options) { 547 var scope = def.scope; 548 var in_use = names_in_use(scope, options); 549 var holes = scope.cname_holes; 550 var names = new Dictionary(); 551 var scopes = [ scope ]; 552 def.forEach(function(sym) { 553 var scope = sym.scope; 554 do { 555 if (member(scope, scopes)) break; 556 names_in_use(scope, options).each(function(marker, name) { 557 names.set(name, marker); 558 }); 559 scopes.push(scope); 560 } while (scope = scope.parent_scope); 561 }); 562 var name; 563 for (var i = 0; i < holes.length; i++) { 564 name = base54(holes[i]); 565 if (names.has(name)) continue; 566 holes.splice(i, 1); 567 in_use.set(name, true); 568 return name; 569 } 570 while (true) { 571 name = base54(++scope.cname); 572 if (in_use.has(name) || RESERVED_WORDS[name] || options.reserved.has[name]) continue; 573 if (!names.has(name)) break; 574 holes.push(scope.cname); 575 } 576 in_use.set(name, true); 577 return name; 578} 579 580AST_Symbol.DEFMETHOD("unmangleable", function(options) { 581 var def = this.definition(); 582 return !def || def.unmangleable(options); 583}); 584 585// labels are always mangleable 586AST_Label.DEFMETHOD("unmangleable", return_false); 587 588AST_Symbol.DEFMETHOD("definition", function() { 589 return this.thedef; 590}); 591 592function _default_mangler_options(options) { 593 options = defaults(options, { 594 eval : false, 595 ie : false, 596 keep_fargs : false, 597 keep_fnames : false, 598 reserved : [], 599 toplevel : false, 600 v8 : false, 601 webkit : false, 602 }); 603 if (!Array.isArray(options.reserved)) options.reserved = []; 604 // Never mangle `arguments` 605 push_uniq(options.reserved, "arguments"); 606 options.reserved.has = makePredicate(options.reserved); 607 return options; 608} 609 610// We only need to mangle declaration nodes. Special logic wired into the code 611// generator will display the mangled name if it is present (and for 612// `AST_SymbolRef`s it will use the mangled name of the `AST_SymbolDeclaration` 613// that it points to). 614AST_Toplevel.DEFMETHOD("mangle_names", function(options) { 615 options = _default_mangler_options(options); 616 if (options.cache && options.cache.props) { 617 var mangled_names = names_in_use(this, options); 618 options.cache.props.each(function(mangled_name) { 619 mangled_names.set(mangled_name, true); 620 }); 621 } 622 var cutoff = 36; 623 var lname = -1; 624 var redefined = []; 625 var tw = new TreeWalker(function(node, descend) { 626 var save_nesting; 627 if (node instanceof AST_BlockScope) { 628 // `lname` is incremented when we get to the `AST_Label` 629 if (node instanceof AST_LabeledStatement) save_nesting = lname; 630 if (options.webkit && node instanceof AST_IterationStatement && node.init instanceof AST_Let) { 631 node.init.definitions.forEach(function(defn) { 632 defn.name.match_symbol(function(sym) { 633 if (!(sym instanceof AST_SymbolLet)) return; 634 var def = sym.definition(); 635 var scope = sym.scope.parent_scope; 636 var redef = scope.def_variable(sym); 637 sym.thedef = def; 638 scope.to_mangle.push(redef); 639 def.redefined = function() { 640 return redef; 641 }; 642 }); 643 }, true); 644 } 645 var to_mangle = node.to_mangle = []; 646 node.variables.each(function(def) { 647 if (!defer_redef(def)) to_mangle.push(def); 648 }); 649 descend(); 650 if (options.cache && node instanceof AST_Toplevel) { 651 node.globals.each(mangle); 652 } 653 if (node instanceof AST_Defun && tw.has_directive("use asm")) { 654 var sym = new AST_SymbolRef(node.name); 655 sym.scope = node; 656 sym.reference(options); 657 } 658 if (to_mangle.length > cutoff) { 659 var indices = to_mangle.map(function(def, index) { 660 return index; 661 }).sort(function(i, j) { 662 return to_mangle[j].references.length - to_mangle[i].references.length || i - j; 663 }); 664 to_mangle = indices.slice(0, cutoff).sort(function(i, j) { 665 return i - j; 666 }).map(function(index) { 667 return to_mangle[index]; 668 }).concat(indices.slice(cutoff).sort(function(i, j) { 669 return i - j; 670 }).map(function(index) { 671 return to_mangle[index]; 672 })); 673 } 674 to_mangle.forEach(mangle); 675 if (node instanceof AST_LabeledStatement && !(options.v8 && in_label(tw))) lname = save_nesting; 676 return true; 677 } 678 if (node instanceof AST_Label) { 679 var name; 680 do { 681 name = base54(++lname); 682 } while (RESERVED_WORDS[name]); 683 node.mangled_name = name; 684 return true; 685 } 686 }); 687 this.walk(tw); 688 redefined.forEach(mangle); 689 690 function mangle(def) { 691 if (options.reserved.has[def.name]) return; 692 def.mangle(options); 693 } 694 695 function defer_redef(def) { 696 var sym = def.orig[0]; 697 var redef = def.redefined(); 698 if (!redef) { 699 if (!(sym instanceof AST_SymbolConst)) return false; 700 var scope = def.scope.resolve(); 701 if (def.scope === scope) return false; 702 if (def.scope.parent_scope.find_variable(sym.name)) return false; 703 redef = scope.def_variable(sym); 704 scope.to_mangle.push(redef); 705 } 706 redefined.push(def); 707 def.references.forEach(reference); 708 if (sym instanceof AST_SymbolCatch || sym instanceof AST_SymbolConst) { 709 reference(sym); 710 def.redefined = function() { 711 return redef; 712 }; 713 } 714 return true; 715 716 function reference(sym) { 717 sym.thedef = redef; 718 sym.reference(options); 719 sym.thedef = def; 720 } 721 } 722 723 function in_label(tw) { 724 var level = 0, parent; 725 while (parent = tw.parent(level++)) { 726 if (parent instanceof AST_Block) return parent instanceof AST_Toplevel && !options.toplevel; 727 if (parent instanceof AST_LabeledStatement) return true; 728 } 729 } 730}); 731 732AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) { 733 var cache = options.cache && options.cache.props; 734 var avoid = Object.create(RESERVED_WORDS); 735 options.reserved.forEach(to_avoid); 736 this.globals.each(add_def); 737 this.walk(new TreeWalker(function(node) { 738 if (node instanceof AST_BlockScope) node.variables.each(add_def); 739 })); 740 return avoid; 741 742 function to_avoid(name) { 743 avoid[name] = true; 744 } 745 746 function add_def(def) { 747 var name = def.name; 748 if (def.global && cache && cache.has(name)) name = cache.get(name); 749 else if (!def.unmangleable(options)) return; 750 to_avoid(name); 751 } 752}); 753 754AST_Toplevel.DEFMETHOD("expand_names", function(options) { 755 base54.reset(); 756 base54.sort(); 757 options = _default_mangler_options(options); 758 var avoid = this.find_colliding_names(options); 759 var cname = 0; 760 this.globals.each(rename); 761 this.walk(new TreeWalker(function(node) { 762 if (node instanceof AST_BlockScope) node.variables.each(rename); 763 })); 764 765 function next_name() { 766 var name; 767 do { 768 name = base54(cname++); 769 } while (avoid[name]); 770 return name; 771 } 772 773 function rename(def) { 774 if (def.global && options.cache) return; 775 if (def.unmangleable(options)) return; 776 if (options.reserved.has[def.name]) return; 777 var redef = def.redefined(); 778 var name = redef ? redef.rename || redef.name : next_name(); 779 def.rename = name; 780 def.forEach(function(sym) { 781 if (sym.definition() === def) sym.name = name; 782 }); 783 } 784}); 785 786AST_Node.DEFMETHOD("tail_node", return_this); 787AST_Sequence.DEFMETHOD("tail_node", function() { 788 return this.expressions[this.expressions.length - 1]; 789}); 790 791AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) { 792 options = _default_mangler_options(options); 793 base54.reset(); 794 var fn = AST_Symbol.prototype.add_source_map; 795 try { 796 AST_Symbol.prototype.add_source_map = function() { 797 if (!this.unmangleable(options)) base54.consider(this.name, -1); 798 }; 799 if (options.properties) { 800 AST_Dot.prototype.add_source_map = function() { 801 base54.consider(this.property, -1); 802 }; 803 AST_Sub.prototype.add_source_map = function() { 804 skip_string(this.property); 805 }; 806 } 807 base54.consider(this.print_to_string(), 1); 808 } finally { 809 AST_Symbol.prototype.add_source_map = fn; 810 delete AST_Dot.prototype.add_source_map; 811 delete AST_Sub.prototype.add_source_map; 812 } 813 base54.sort(); 814 815 function skip_string(node) { 816 if (node instanceof AST_String) { 817 base54.consider(node.value, -1); 818 } else if (node instanceof AST_Conditional) { 819 skip_string(node.consequent); 820 skip_string(node.alternative); 821 } else if (node instanceof AST_Sequence) { 822 skip_string(node.tail_node()); 823 } 824 } 825}); 826 827var base54 = (function() { 828 var freq = Object.create(null); 829 function init(chars) { 830 var array = []; 831 for (var i = 0; i < chars.length; i++) { 832 var ch = chars[i]; 833 array.push(ch); 834 freq[ch] = -1e-2 * i; 835 } 836 return array; 837 } 838 var digits = init("0123456789"); 839 var leading = init("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_"); 840 var chars, frequency; 841 function reset() { 842 chars = null; 843 frequency = Object.create(freq); 844 } 845 base54.consider = function(str, delta) { 846 for (var i = str.length; --i >= 0;) { 847 frequency[str[i]] += delta; 848 } 849 }; 850 function compare(a, b) { 851 return frequency[b] - frequency[a]; 852 } 853 base54.sort = function() { 854 chars = leading.sort(compare).concat(digits).sort(compare); 855 }; 856 base54.reset = reset; 857 reset(); 858 function base54(num) { 859 var ret = leading[num % 54]; 860 for (num = Math.floor(num / 54); --num >= 0; num >>= 6) { 861 ret += chars[num & 0x3F]; 862 } 863 return ret; 864 } 865 return base54; 866})(); 867