function AST_Accessor(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Array(props){ if (props) { this.end = props.end;this.start = props.start;this.elements = props.elements;}}
n/a
function AST_Assign(props){ if (props) { this.end = props.end;this.start = props.start;this.right = props.right;this.operator = props.operator;this.left = props.left;}}
n/a
function AST_Atom(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Binary(props){ if (props) { this.end = props.end;this.start = props.start;this.right = props.right;this.operator = props.operator;this.left = props.left;}}
n/a
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_BlockStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Boolean(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Break(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
function AST_Call(props){ if (props) { this.end = props.end;this.start = props.start;this.args = props.args;this.expression = props .expression;}}
n/a
function AST_Case(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props .expression;}}
n/a
function AST_Catch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.argname = props .argname;}}
n/a
function AST_Conditional(props){ if (props) { this.end = props.end;this.start = props.start;this.alternative = props.alternative ;this.consequent = props.consequent;this.condition = props.condition;}}
n/a
function AST_Const(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions;}}
n/a
function AST_Constant(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Continue(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
function AST_DWLoop(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
function AST_Debugger(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Default(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Definitions(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions ;}}
n/a
function AST_Defun(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props. cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Directive(props){ if (props) { this.end = props.end;this.start = props.start;this.quote = props.quote;this.scope = props.scope;this.value = props.value;}}
n/a
function AST_Do(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
function AST_Dot(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
function AST_EmptyStatement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Exit(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_False(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Finally(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_For(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.step = props.step ;this.condition = props.condition;this.init = props.init;}}
n/a
function AST_ForIn(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.object = props .object;this.name = props.name;this.init = props.init;}}
n/a
function AST_Function(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Hole(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_If(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.alternative = props .alternative;this.condition = props.condition;}}
n/a
function AST_Infinity(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_IterationStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Jump(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Label(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;this.references = props.references;this.initialize();}}
n/a
function AST_LabelRef(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
n/a
function AST_LabeledStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.label = props.label;}}
n/a
function AST_Lambda(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_LoopControl(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
function AST_NaN(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_New(props){ if (props) { this.end = props.end;this.start = props.start;this.args = props.args;this.expression = props .expression;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Null(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Number(props){ if (props) { this.end = props.end;this.start = props.start;this.literal = props.literal;this.value = props.value;}}
n/a
function AST_Object(props){ if (props) { this.end = props.end;this.start = props.start;this.properties = props.properties;}}
n/a
function AST_ObjectGetter(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
function AST_ObjectKeyVal(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;this.quote = props.quote;}}
n/a
function AST_ObjectProperty(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
function AST_ObjectSetter(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
function AST_PropAccess(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
function AST_RegExp(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_Return(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_Scope(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props. cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;}}
n/a
function AST_Seq(props){ if (props) { this.end = props.end;this.start = props.start;this.cdr = props.cdr;this.car = props.car;}}
n/a
function AST_SimpleStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_StatementWithBody(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_String(props){ if (props) { this.end = props.end;this.start = props.start;this.quote = props.quote;this.value = props .value;}}
n/a
function AST_Sub(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
function AST_Switch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props.expression;}}
n/a
function AST_SwitchBranch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Symbol(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
function AST_SymbolAccessor(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
n/a
function AST_SymbolCatch(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolConst(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolDeclaration(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this .name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolDefun(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolFunarg(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolLambda(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolRef(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
n/a
function AST_SymbolVar(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_This(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
function AST_Throw(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_Token(props){ if (props) { this.raw = props.raw;this.file = props.file;this.comments_before = props.comments_before ;this.nlb = props.nlb;this.endpos = props.endpos;this.endcol = props.endcol;this.endline = props.endline;this.pos = props.pos;this .col = props.col;this.line = props.line;this.value = props.value;this.type = props.type;}}
n/a
function AST_Toplevel(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.globals = props .globals;}}
n/a
function AST_True(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Try(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.bfinally = props .bfinally;this.bcatch = props.bcatch;}}
n/a
function AST_Unary(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression;this.operator = props.operator;}}
n/a
function AST_UnaryPostfix(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression; this.operator = props.operator;}}
n/a
function AST_UnaryPrefix(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression;this .operator = props.operator;}}
n/a
function AST_Undefined(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Var(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions;}}
n/a
function AST_VarDef(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.name = props .name;}}
n/a
function AST_While(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
function AST_With(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props .expression;}}
n/a
function Compressor(options, false_by_default) { if (!(this instanceof Compressor)) return new Compressor(options, false_by_default); TreeTransformer.call(this, this.before, this.after); this.options = defaults(options, { angular : false, booleans : !false_by_default, cascade : !false_by_default, collapse_vars : !false_by_default, comparisons : !false_by_default, conditionals : !false_by_default, dead_code : !false_by_default, drop_console : false, drop_debugger : !false_by_default, evaluate : !false_by_default, expression : false, global_defs : {}, hoist_funs : !false_by_default, hoist_vars : false, if_return : !false_by_default, join_vars : !false_by_default, keep_fargs : true, keep_fnames : false, keep_infinity : false, loops : !false_by_default, negate_iife : !false_by_default, passes : 1, properties : !false_by_default, pure_getters : !false_by_default && "strict", pure_funcs : null, reduce_vars : !false_by_default, screw_ie8 : true, sequences : !false_by_default, side_effects : !false_by_default, switches : !false_by_default, top_retain : null, toplevel : !!(options && options["top_retain"]), unsafe : false, unsafe_comps : false, unsafe_math : false, unsafe_proto : false, unused : !false_by_default, warnings : true, }, true); var pure_funcs = this.options["pure_funcs"]; if (typeof pure_funcs == "function") { this.pure_funcs = pure_funcs; } else { this.pure_funcs = pure_funcs ? function(node) { return pure_funcs.indexOf(node.expression.print_to_string()) < 0; } : return_true; } var top_retain = this.options["top_retain"]; if (top_retain instanceof RegExp) { this.top_retain = function(def) { return top_retain.test(def.name); }; } else if (typeof top_retain == "function") { this.top_retain = top_retain; } else if (top_retain) { if (typeof top_retain == "string") { top_retain = top_retain.split(/,/); } this.top_retain = function(def) { return top_retain.indexOf(def.name) >= 0; }; } var sequences = this.options["sequences"]; this.sequences_limit = sequences == 1 ? 200 : sequences | 0; this.warnings_produced = {}; }
...
```javascript
function uglify(ast, options, mangle) {
// Conversion from SpiderMonkey AST to internal format
var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
// Compression
uAST.figure_out_scope();
uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional)
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
...
function DefaultsError(msg, defs) { this.message = msg; this.defs = defs; }
n/a
function Dictionary() { this._values = Object.create(null); this._size = 0; }
n/a
function JS_Parse_Error(message, filename, line, col, pos) { this.message = message; this.filename = filename; this.line = line; this.col = col; this.pos = pos; }
n/a
function MAP(a, f, backwards) { var ret = [], top = [], i; function doit() { var val = f(a[i], i); var is_last = val instanceof Last; if (is_last) val = val.v; if (val instanceof AtTop) { val = val.v; if (val instanceof Splice) { top.push.apply(top, backwards ? val.v.slice().reverse() : val.v); } else { top.push(val); } } else if (val !== skip) { if (val instanceof Splice) { ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v); } else { ret.push(val); } } return is_last; }; if (a instanceof Array) { if (backwards) { for (i = a.length; --i >= 0;) if (doit()) break; ret.reverse(); top.reverse(); } else { for (i = 0; i < a.length; ++i) if (doit()) break; } } else { for (i in a) if (HOP(a, i)) if (doit()) break; } return top.concat(ret); }
n/a
function OutputStream(options) { options = defaults(options, { ascii_only : false, beautify : false, bracketize : false, comments : false, indent_level : 4, indent_start : 0, inline_script : true, keep_quoted_props: false, max_line_len : false, preamble : null, preserve_line : false, quote_keys : false, quote_style : 0, screw_ie8 : true, semicolons : true, shebang : true, source_map : null, space_colon : true, unescape_regexps : false, width : 80, wrap_iife : false, }, true); // Convert comment option to RegExp if neccessary and set up comments filter var comment_filter = return_false; // Default case, throw all comments away if (options.comments) { var comments = options.comments; if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) { var regex_pos = options.comments.lastIndexOf("/"); comments = new RegExp( options.comments.substr(1, regex_pos - 1), options.comments.substr(regex_pos + 1) ); } if (comments instanceof RegExp) { comment_filter = function(comment) { return comment.type != "comment5" && comments.test(comment.value); }; } else if (typeof comments === "function") { comment_filter = function(comment) { return comment.type != "comment5" && comments(this, comment); }; } else if (comments === "some") { comment_filter = is_some_comments; } else { // NOTE includes "all" option comment_filter = return_true; } } var indentation = 0; var current_col = 0; var current_line = 1; var current_pos = 0; var OUTPUT = ""; function to_ascii(str, identifier) { return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) { var code = ch.charCodeAt(0).toString(16); if (code.length <= 2 && !identifier) { while (code.length < 2) code = "0" + code; return "\\x" + code; } else { while (code.length < 4) code = "0" + code; return "\\u" + code; } }); }; function make_string(str, quote) { var dq = 0, sq = 0; str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i){ switch (s) { case '"': ++dq; return '"'; case "'": ++sq; return "'"; case "\\": return "\\\\"; case "\n": return "\\n"; case "\r": return "\\r"; case "\t": return "\\t"; case "\b": return "\\b"; case "\f": return "\\f"; case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B"; case "\u2028": return "\\u2028"; case "\u2029": return "\\u2029"; case "\ufeff": return "\\ufeff"; case "\0": return /[0-7]/.test(str.charAt(i+1)) ? "\\x00" : "\\0"; } return s; }); function quote_single() { return "'" + str.replace(/\x27/g, "\\'") + "'"; } function quote_double() { return '"' + str.replace(/\x22/g, '\\"') + '"'; } if (options.ascii_only) str = to_ascii(str); switch (options.quote_style) { case 1: return quote_single(); case 2: return quote_double(); case 3: return quote == "'" ? quote_single() : quote_double(); default: return dq > sq ? quote_single() : quote_double(); } }; function encode_string(str, quote) { var ret = make_string(str, quote); if (options.inlin ...
...
```
#### Generating output
AST nodes have a `print` method that takes an output stream. Essentially,
to generate code you do this:
```javascript
var stream = UglifyJS.OutputStream(options);
compressed_ast.print(stream);
var code = stream.toString(); // this is your minified code
```
or, for a shortcut you can do:
```javascript
var code = compressed_ast.print_to_string(options);
...
function SourceMap(options) { options = defaults(options, { file : null, root : null, orig : null, orig_line_diff : 0, dest_line_diff : 0, }); var generator = new MOZ_SourceMap.SourceMapGenerator({ file : options.file, sourceRoot : options.root }); var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig); if (orig_map && Array.isArray(options.orig.sources)) { orig_map._sources.toArray().forEach(function(source) { var sourceContent = orig_map.sourceContentFor(source, true); if (sourceContent) { generator.setSourceContent(source, sourceContent); } }); } function add(source, gen_line, gen_col, orig_line, orig_col, name) { if (orig_map) { var info = orig_map.originalPositionFor({ line: orig_line, column: orig_col }); if (info.source === null) { return; } source = info.source; orig_line = info.line; orig_col = info.column; name = info.name || name; } generator.addMapping({ generated : { line: gen_line + options.dest_line_diff, column: gen_col }, original : { line: orig_line + options.orig_line_diff, column: orig_col }, source : source, name : name }); }; return { add : add, get : function() { return generator }, toString : function() { return JSON.stringify(generator.toJSON()); } }; }
...
You need to pass the `source_map` argument when calling `print`. It needs
to be a `SourceMap` object (which is a thin wrapper on top of the
[source-map][source-map] library).
Example:
```javascript
var source_map = UglifyJS.SourceMap(source_map_options);
var stream = UglifyJS.OutputStream({
...
source_map: source_map
});
compressed_ast.print(stream);
var code = stream.toString();
...
function SymbolDef(scope, index, orig) { this.name = orig.name; this.orig = [ orig ]; this.scope = scope; this.references = []; this.global = false; this.mangled_name = null; this.undeclared = false; this.index = index; this.id = SymbolDef.next_id++; }
n/a
function TreeTransformer(before, after) { TreeWalker.call(this); this.before = before; this.after = after; }
n/a
function TreeWalker(callback) { this.visit = callback; this.stack = []; this.directives = Object.create(null); }
...
console.log("%s", JSON.stringify(output, null, 2));
}
function getProps(filename) {
var code = fs.readFileSync(filename, "utf8");
var ast = U2.parse(code);
ast.walk(new U2.TreeWalker(function(node){
if (node instanceof U2.AST_ObjectKeyVal) {
add(node.key);
}
else if (node instanceof U2.AST_ObjectProperty) {
add(node.key.name);
}
else if (node instanceof U2.AST_Dot) {
...
function base54(num) { var ret = "", base = 54; num++; do { num--; ret += String.fromCharCode(chars[num % base]); num = Math.floor(num / base); base = 64; } while (num > 0); return ret; }
n/a
function defaults(args, defs, croak) { if (args === true) args = {}; var ret = args || {}; if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i)) DefaultsError.croak("`" + i + "` is not a supported option", defs); for (var i in defs) if (HOP(defs, i)) { ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; } return ret; }
n/a
describe_ast = function () { var out = UglifyJS.OutputStream({ beautify: true }); function doitem(ctor) { out.print("AST_" + ctor.TYPE); var props = ctor.SELF_PROPS.filter(function(prop){ return !/^\$/.test(prop); }); if (props.length > 0) { out.space(); out.with_parens(function(){ props.forEach(function(prop, i){ if (i) out.space(); out.print(prop); }); }); } if (ctor.documentation) { out.space(); out.print_string(ctor.documentation); } if (ctor.SUBCLASSES.length > 0) { out.space(); out.with_block(function(){ ctor.SUBCLASSES.forEach(function(ctor, i){ out.indent(); doitem(ctor); out.newline(); }); }); } }; doitem(UglifyJS.AST_Node); return out + ""; }
n/a
function is_identifier(name) { return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name); }
n/a
function mangle_properties(ast, options) { options = defaults(options, { cache: null, debug: false, ignore_quoted: false, only_cache: false, regex: null, reserved: null, }); var reserved = options.reserved; if (reserved == null) reserved = find_builtins(); var cache = options.cache; if (cache == null) { cache = { cname: -1, props: new Dictionary() }; } var regex = options.regex; var ignore_quoted = options.ignore_quoted; // note debug is either false (disabled), or a string of the debug suffix to use (enabled). // note debug may be enabled as an empty string, which is falsey. Also treat passing 'true' // the same as passing an empty string. var debug = (options.debug !== false); var debug_name_suffix; if (debug) { debug_name_suffix = (options.debug === true ? "" : options.debug); } var names_to_mangle = []; var unmangleable = []; var ignored = {}; // step 1: find candidates to mangle ast.walk(new TreeWalker(function(node){ if (node instanceof AST_ObjectKeyVal) { add(node.key, ignore_quoted && node.quote); } else if (node instanceof AST_ObjectProperty) { // setter or getter, since KeyVal is handled above add(node.key.name); } else if (node instanceof AST_Dot) { add(node.property); } else if (node instanceof AST_Sub) { addStrings(node.property, ignore_quoted); } })); // step 2: transform the tree, renaming properties return ast.transform(new TreeTransformer(function(node){ if (node instanceof AST_ObjectKeyVal) { if (!(ignore_quoted && node.quote)) node.key = mangle(node.key); } else if (node instanceof AST_ObjectProperty) { // setter or getter node.key.name = mangle(node.key.name); } else if (node instanceof AST_Dot) { node.property = mangle(node.property); } else if (node instanceof AST_Sub) { if (!ignore_quoted) node.property = mangleStrings(node.property); } // else if (node instanceof AST_String) { // if (should_mangle(node.value)) { // AST_Node.warn( // "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", { // file : node.start.file, // line : node.start.line, // col : node.start.col, // prop : node.value // } // ); // } // } })); // only function declarations after this line function can_mangle(name) { if (unmangleable.indexOf(name) >= 0) return false; if (reserved.indexOf(name) >= 0) return false; if (options.only_cache) { return cache.props.has(name); } if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false; return true; } function should_mangle(name) { if (ignore_quoted && name in ignored) return false; if (regex && !regex.test(name)) return false; if (reserved.indexOf(name) >= 0) return false; return cache.props.has(name) || names_to_mangle.indexOf(name) >= 0; } function add(name, ignore) { if (ignore) { ignored[name] = true; return; } if (can_mangle(name)) push_uniq(names_to_mangle, name); if (!should_mangle(name)) { push_uniq(unmangleable, name); } } function mangle(name) { if (!should_mangle(name)) { return name; } var mangled = cache.props.get(name); if (!mangled) { if (debug) { // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_. ...
n/a
function merge(obj, ext) { var count = 0; for (var i in ext) if (HOP(ext, i)) { obj[i] = ext[i]; count++; } return count; }
n/a
minify = function (files, options) { options = UglifyJS.defaults(options, { compress : {}, fromString : false, inSourceMap : null, mangle : {}, mangleProperties : false, nameCache : null, outFileName : null, output : null, outSourceMap : null, parse : {}, sourceMapInline : false, sourceMapUrl : null, sourceRoot : null, spidermonkey : false, warnings : false, }); UglifyJS.base54.reset(); var inMap = options.inSourceMap; if (typeof inMap == "string" && inMap != "inline") { inMap = JSON.parse(fs.readFileSync(inMap, "utf8")); } // 1. parse var toplevel = null, sourcesContent = {}; if (options.spidermonkey) { if (inMap == "inline") { throw new Error("inline source map only works with built-in parser"); } toplevel = UglifyJS.AST_Node.from_mozilla_ast(files); } else { function addFile(file, fileUrl) { var code = options.fromString ? file : fs.readFileSync(file, "utf8"); if (inMap == "inline") { inMap = read_source_map(code); } sourcesContent[fileUrl] = code; toplevel = UglifyJS.parse(code, { filename: fileUrl, toplevel: toplevel, bare_returns: options.parse ? options.parse.bare_returns : undefined }); } if (!options.fromString) { files = UglifyJS.simple_glob(files); if (inMap == "inline" && files.length > 1) { throw new Error("inline source map only works with singular input"); } } [].concat(files).forEach(function (files, i) { if (typeof files === 'string') { addFile(files, options.fromString ? i : files); } else { for (var fileUrl in files) { addFile(files[fileUrl], fileUrl); } } }); } if (options.wrap) { toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll); } // 2. compress if (options.compress) { var compress = { warnings: options.warnings }; UglifyJS.merge(compress, options.compress); toplevel.figure_out_scope(options.mangle); var sq = UglifyJS.Compressor(compress); toplevel = sq.compress(toplevel); } // 3. mangle properties if (options.mangleProperties || options.nameCache) { options.mangleProperties.cache = UglifyJS.readNameCache(options.nameCache, "props"); toplevel = UglifyJS.mangle_properties(toplevel, options.mangleProperties); UglifyJS.writeNameCache(options.nameCache, "props", options.mangleProperties.cache); } // 4. mangle if (options.mangle) { toplevel.figure_out_scope(options.mangle); toplevel.compute_char_frequency(options.mangle); toplevel.mangle_names(options.mangle); } // 5. output var output = { max_line_len: 32000 }; if (options.outSourceMap || options.sourceMapInline) { output.source_map = UglifyJS.SourceMap({ // prefer outFileName, otherwise use outSourceMap without .map suffix file: options.outFileName || (typeof options.outSourceMap === 'string' ? options.outSourceMap.replace(/\.map$/i, '') : null), orig: inMap, root: options.sourceRoot }); if (options.sourceMapIncludeSources) { for (var file in sourcesContent) { if (sourcesContent.hasOwnProperty(file)) { output.source_map.get().setSourceContent(file, sourcesContent[file]); } } } } if (options.output) { UglifyJS.merge(output, options.output); } var stream = UglifyJS.OutputStream(output); toplevel.print(stream); var source_map = output.source ...
...
<a name="codegen-options"></a>
#### Conditional compilation, API
You can also use conditional compilation via the programmatic API. With the difference that the
property name is `global_defs` and is a compressor property:
```js
uglifyJS.minify([ "input.js"], {
compress: {
dead_code: true,
global_defs: {
DEBUG: false
}
}
});
...
function parse($TEXT, options) { options = defaults(options, { bare_returns : false, cli : false, expression : false, filename : null, html5_comments : true, shebang : true, strict : false, toplevel : null, }); var S = { input : (typeof $TEXT == "string" ? tokenizer($TEXT, options.filename, options.html5_comments, options.shebang) : $TEXT), token : null, prev : null, peeked : null, in_function : 0, in_directives : true, in_loop : 0, labels : [] }; S.token = next(); function is(type, value) { return is_token(S.token, type, value); }; function peek() { return S.peeked || (S.peeked = S.input()); }; function next() { S.prev = S.token; if (S.peeked) { S.token = S.peeked; S.peeked = null; } else { S.token = S.input(); } S.in_directives = S.in_directives && ( S.token.type == "string" || is("punc", ";") ); return S.token; }; function prev() { return S.prev; }; function croak(msg, line, col, pos) { var ctx = S.input.context(); js_error(msg, ctx.filename, line != null ? line : ctx.tokline, col != null ? col : ctx.tokcol, pos != null ? pos : ctx.tokpos); }; function token_error(token, msg) { croak(msg, token.line, token.col); }; function unexpected(token) { if (token == null) token = S.token; token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")"); }; function expect_token(type, val) { if (is(type, val)) { return next(); } token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»"); }; function expect(punc) { return expect_token("punc", punc); }; function can_insert_semicolon() { return !options.strict && ( S.token.nlb || is("eof") || is("punc", "}") ); }; function semicolon(optional) { if (is("punc", ";")) next(); else if (!optional && !can_insert_semicolon()) unexpected(); }; function parenthesised() { expect("("); var exp = expression(true); expect(")"); return exp; }; function embed_tokens(parser) { return function() { var start = S.token; var expr = parser(); var end = prev(); expr.start = start; expr.end = end; return expr; }; }; function handle_regexp() { if (is("operator", "/") || is("operator", "/=")) { S.peeked = null; S.token = S.input(S.token.value.substr(1)); // force regexp } }; var statement = embed_tokens(function() { var tmp; handle_regexp(); switch (S.token.type) { case "string": var dir = false; if (S.in_directives === true) { if ((is_token(peek(), "punc", ";") || peek().nlb) && S.token.raw.indexOf("\\") === -1) { S.input.add_directive(S.token.value); } else { S.in_directives = false; } } var dir = S.in_directives, stat = simple_statement(); if (dir) { return new AST_Directive({ start : stat.body.start, end : stat.body.end, quote : stat.body.quote, value : stat.body.value, }); } return stat; case "num": case "regexp": case "operator": case "atom": ...
...
```
If your input source map is not in a file, you can pass it in as an object
using the `inSourceMap` argument:
```javascript
var result = UglifyJS.minify("compiled.js", {
inSourceMap: JSON.parse(my_source_map_string),
outSourceMap: "minified.js.map"
});
```
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
no sense otherwise).
...
function push_uniq(array, el) { if (array.indexOf(el) < 0) array.push(el); }
n/a
readDefaultReservedFile = function (reserved) { return readReservedFile(require.resolve("./domprops.json"), reserved); }
n/a
readNameCache = function (filename, key) { var cache = null; if (filename) { try { var cache = fs.readFileSync(filename, "utf8"); cache = JSON.parse(cache)[key]; if (!cache) throw "init"; cache.props = UglifyJS.Dictionary.fromObject(cache.props); } catch(ex) { cache = { cname: -1, props: new UglifyJS.Dictionary() }; } } return cache; }
n/a
function readReservedFile(filename, reserved) { if (!reserved) { reserved = { vars: [], props: [] }; } var data = fs.readFileSync(filename, "utf8"); data = JSON.parse(data); if (data.vars) { data.vars.forEach(function(name){ UglifyJS.push_uniq(reserved.vars, name); }); } if (data.props) { data.props.forEach(function(name){ UglifyJS.push_uniq(reserved.props, name); }); } return reserved; }
n/a
function simple_glob(glob) { if (Array.isArray(glob)) { return [].concat.apply([], glob.map(simple_glob)); } if (glob.match(/\*|\?/)) { var dir = path.dirname(glob); try { var entries = fs.readdirSync(dir); } catch (ex) {} if (entries) { var pattern = "^" + path.basename(glob) .replace(/[.+^$[\]\\(){}]/g, "\\$&") .replace(/\*/g, "[^/\\\\]*") .replace(/\?/g, "[^/\\\\]") + "$"; var mod = process.platform === "win32" ? "i" : ""; var rx = new RegExp(pattern, mod); var results = entries.filter(function(name) { return rx.test(name); }).map(function(name) { return path.join(dir, name); }); if (results.length) return results; } } return [ glob ]; }
n/a
function string_template(text, props) { return text.replace(/\{(.+?)\}/g, function(str, p){ return props && props[p]; }); }
n/a
function tokenizer($TEXT, filename, html5_comments, shebang) { var S = { text : $TEXT, filename : filename, pos : 0, tokpos : 0, line : 1, tokline : 0, col : 0, tokcol : 0, newline_before : false, regex_allowed : false, comments_before : [], directives : {}, directive_stack : [] }; function peek() { return S.text.charAt(S.pos); }; function next(signal_eof, in_string) { var ch = S.text.charAt(S.pos++); if (signal_eof && !ch) throw EX_EOF; if (NEWLINE_CHARS(ch)) { S.newline_before = S.newline_before || !in_string; ++S.line; S.col = 0; if (!in_string && ch == "\r" && peek() == "\n") { // treat a \r\n sequence as a single \n ++S.pos; ch = "\n"; } } else { ++S.col; } return ch; }; function forward(i) { while (i-- > 0) next(); }; function looking_at(str) { return S.text.substr(S.pos, str.length) == str; }; function find_eol() { var text = S.text; for (var i = S.pos, n = S.text.length; i < n; ++i) { var ch = text[i]; if (NEWLINE_CHARS(ch)) return i; } return -1; }; function find(what, signal_eof) { var pos = S.text.indexOf(what, S.pos); if (signal_eof && pos == -1) throw EX_EOF; return pos; }; function start_token() { S.tokline = S.line; S.tokcol = S.col; S.tokpos = S.pos; }; var prev_was_dot = false; function token(type, value, is_comment) { S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) || (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) || (type == "punc" && PUNC_BEFORE_EXPRESSION(value))); prev_was_dot = (type == "punc" && value == "."); var ret = { type : type, value : value, line : S.tokline, col : S.tokcol, pos : S.tokpos, endline : S.line, endcol : S.col, endpos : S.pos, nlb : S.newline_before, file : filename }; if (/^(?:num|string|regexp)$/i.test(type)) { ret.raw = $TEXT.substring(ret.pos, ret.endpos); } if (!is_comment) { ret.comments_before = S.comments_before; S.comments_before = []; // make note of any newlines in the comments that came before for (var i = 0, len = ret.comments_before.length; i < len; i++) { ret.nlb = ret.nlb || ret.comments_before[i].nlb; } } S.newline_before = false; return new AST_Token(ret); }; function skip_whitespace() { while (WHITESPACE_CHARS(peek())) next(); }; function read_while(pred) { var ret = "", ch, i = 0; while ((ch = peek()) && pred(ch, i++)) ret += next(); return ret; }; function parse_error(err) { js_error(err, filename, S.tokline, S.tokcol, S.tokpos); }; function read_num(prefix) { var has_e = false, after_e = false, has_x = false, has_dot = prefix == "."; var num = read_while(function(ch, i){ var code = ch.charCodeAt(0); switch (code) { case 120: case 88: // xX return has_x ? false : (has_x = true); case 101: case 69: // eE return has_x ? true : has_e ? false : (has_e = after_e = true); case 45: // - return after_e || (i == 0 && !prefix); case 43: // + return after_e; case (after_e = false, 46): // . return (!has_dot && !has_x && !ha ...
n/a
writeNameCache = function (filename, key, cache) { if (filename) { var data; try { data = fs.readFileSync(filename, "utf8"); data = JSON.parse(data); } catch(ex) { data = {}; } data[key] = { cname: cache.cname, props: cache.props.toObject() }; fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8"); } }
n/a
function AST_Accessor(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Lambda(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Accessor(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function AST_Array(props){ if (props) { this.end = props.end;this.start = props.start;this.elements = props.elements;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Array(props){ if (props) { this.end = props.end;this.start = props.start;this.elements = props.elements;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.with_square(function(){ var a = self.elements, len = a.length; if (len > 0) output.space(); a.forEach(function(exp, i){ if (i) output.comma(); exp.print(output); // If the final element is a hole, we need to make sure it // doesn't look like a trailing comma, by inserting an actual // trailing comma. if (i === len - 1 && exp instanceof AST_Hole) output.comma(); }); if (len > 0) output.space(); }); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function return_false() { return false; }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
_eval = function (compressor){ if (compressor.option("unsafe")) { return this.elements.map(function(element) { return ev(element, compressor); }); } throw def; }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
_walk = function (visitor) { return visitor._visit(this, function(){ var elements = this.elements; for (var i = 0, len = elements.length; i < len; i++) { elements[i]._walk(visitor); } }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
drop_side_effect_free = function (compressor, first_in_statement){ var values = trim(this.elements, compressor, first_in_statement); return values && AST_Seq.from_array(values); }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
has_side_effects = function (compressor){ return any(this.elements, compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Assign(props){ if (props) { this.end = props.end;this.start = props.start;this.right = props.right;this.operator = props.operator;this.left = props.left;}}
n/a
function AST_Binary(props){ if (props) { this.end = props.end;this.start = props.start;this.right = props.right;this.operator = props.operator;this.left = props.left;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Assign(props){ if (props) { this.end = props.end;this.start = props.start;this.right = props.right;this.operator = props.operator;this.left = props.left;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_eq_null = function (pure_getters) { return this.operator == "=" && this.right._eq_null(pure_getters); }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
function return_this() { return this; }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
function return_true() { return true; }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
is_boolean = function (){ return this.operator == "=" && this.right.is_boolean(); }
...
def(AST_Node, return_false);
def(AST_UnaryPrefix, function(){
return member(this.operator, unary_bool);
});
def(AST_Binary, function(){
return member(this.operator, binary_bool) ||
( (this.operator == "&&" || this.operator == "||") &&
this.left.is_boolean() && this.right.is_boolean() );
});
def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_boolean();
});
...
is_number = function (compressor){ return binary(this.operator.slice(0, -1)) || this.operator == "=" && this.right.is_number(compressor); }
...
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function(){
return unary(this.operator);
});
var binary = makePredicate("- * / % & | ^ << >> >>>");
def(AST_Binary, function(compressor){
return binary(this.operator) || this.operator == "+"
&& this.left.is_number(compressor)
&& this.right.is_number(compressor);
});
def(AST_Assign, function(compressor){
return binary(this.operator.slice(0, -1))
|| this.operator == "=" && this.right.is_number(compressor);
});
def(AST_Seq, function(compressor){
...
is_string = function (compressor){ return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor); }
...
def(AST_Node, return_false);
def(AST_String, return_true);
def(AST_UnaryPrefix, function(){
return this.operator == "typeof";
});
def(AST_Binary, function(compressor){
return this.operator == "+" &&
(this.left.is_string(compressor) || this.right.is_string(compressor));
});
def(AST_Assign, function(compressor){
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
def(AST_Seq, function(compressor){
return this.cdr.is_string(compressor);
});
...
needs_parens = function (output){ var p = output.parent(); // !(a = false) → true if (p instanceof AST_Unary) return true; // 1 + (a = 2) + 3 → 6, side effect setting a = 2 if (p instanceof AST_Binary && !(p instanceof AST_Assign)) return true; // (a = func)() —or— new (a = Object)() if (p instanceof AST_Call && p.expression === this) return true; // (a = foo) ? bar : baz if (p instanceof AST_Conditional && p.condition === this) return true; // (a = foo)["prop"] —or— (a = foo).prop if (p instanceof AST_PropAccess && p.expression === this) return true; }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Atom(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Constant(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Null(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_NaN(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Undefined(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Hole(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Infinity(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Boolean(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Atom(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Binary(props){ if (props) { this.end = props.end;this.start = props.start;this.right = props.right;this.operator = props.operator;this.left = props.left;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Assign(props){ if (props) { this.end = props.end;this.start = props.start;this.right = props.right;this.operator = props.operator;this.left = props.left;}}
n/a
function AST_Binary(props){ if (props) { this.end = props.end;this.start = props.start;this.right = props.right;this.operator = props.operator;this.left = props.left;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){
var op = self.operator;
self.left.print(output);
if (op[0] == ">" /* ">>" ">>>" ">" ">=" */
&& self.left instanceof AST_UnaryPostfix
&& self.left.operator == "--") {
// space is mandatory to avoid outputting -->
output.print(" ");
} else {
// the space is optional depending on "beautify"
output.space();
}
output.print(op);
if ((op == "<" || op == "<<")
&& self.right instanceof AST_UnaryPrefix
&& self.right.operator == "!"
&& self.right.expression instanceof AST_UnaryPrefix
&& self.right.expression.operator == "--") {
// space is mandatory to avoid outputting <!--
output.print(" ");
} else {
// the space is optional depending on "beautify"
output.space();
}
self.right.print(output);
}
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_eq_null = function (pure_getters) { switch (this.operator) { case "&&": return this.left._eq_null(pure_getters); case "||": return this.left._eq_null(pure_getters) && this.right._eq_null(pure_getters); default: return false; } }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
_eval = function (c){ var left = this.left, right = this.right, result; switch (this.operator) { case "&&" : result = ev(left, c) && ev(right, c); break; case "||" : result = ev(left, c) || ev(right, c); break; case "|" : result = ev(left, c) | ev(right, c); break; case "&" : result = ev(left, c) & ev(right, c); break; case "^" : result = ev(left, c) ^ ev(right, c); break; case "+" : result = ev(left, c) + ev(right, c); break; case "*" : result = ev(left, c) * ev(right, c); break; case "/" : result = ev(left, c) / ev(right, c); break; case "%" : result = ev(left, c) % ev(right, c); break; case "-" : result = ev(left, c) - ev(right, c); break; case "<<" : result = ev(left, c) << ev(right, c); break; case ">>" : result = ev(left, c) >> ev(right, c); break; case ">>>" : result = ev(left, c) >>> ev(right, c); break; case "==" : result = ev(left, c) == ev(right, c); break; case "===" : result = ev(left, c) === ev(right, c); break; case "!=" : result = ev(left, c) != ev(right, c); break; case "!==" : result = ev(left, c) !== ev(right, c); break; case "<" : result = ev(left, c) < ev(right, c); break; case "<=" : result = ev(left, c) <= ev(right, c); break; case ">" : result = ev(left, c) > ev(right, c); break; case ">=" : result = ev(left, c) >= ev(right, c); break; default: throw def; } if (isNaN(result) && c.find_parent(AST_With)) { // leave original expression as is throw def; } return result; }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.left._walk(visitor); this.right._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
drop_side_effect_free = function (compressor, first_in_statement){ var right = this.right.drop_side_effect_free(compressor); if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement); switch (this.operator) { case "&&": case "||": if (right === this.right) return this; var node = this.clone(); node.right = right; return node; default: var left = this.left.drop_side_effect_free(compressor, first_in_statement); if (!left) return this.right.drop_side_effect_free(compressor, first_in_statement); return make_node(AST_Seq, this, { car: left, cdr: right }); } }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
has_side_effects = function (compressor){ return this.left.has_side_effects(compressor) || this.right.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
is_boolean = function (){ return member(this.operator, binary_bool) || ( (this.operator == "&&" || this.operator == "||") && this.left.is_boolean() && this.right.is_boolean() ); }
...
def(AST_Node, return_false);
def(AST_UnaryPrefix, function(){
return member(this.operator, unary_bool);
});
def(AST_Binary, function(){
return member(this.operator, binary_bool) ||
( (this.operator == "&&" || this.operator == "||") &&
this.left.is_boolean() && this.right.is_boolean() );
});
def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_boolean();
});
...
is_number = function (compressor){ return binary(this.operator) || this.operator == "+" && this.left.is_number(compressor) && this.right.is_number(compressor); }
...
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function(){
return unary(this.operator);
});
var binary = makePredicate("- * / % & | ^ << >> >>>");
def(AST_Binary, function(compressor){
return binary(this.operator) || this.operator == "+"
&& this.left.is_number(compressor)
&& this.right.is_number(compressor);
});
def(AST_Assign, function(compressor){
return binary(this.operator.slice(0, -1))
|| this.operator == "=" && this.right.is_number(compressor);
});
def(AST_Seq, function(compressor){
...
is_string = function (compressor){ return this.operator == "+" && (this.left.is_string(compressor) || this.right.is_string(compressor)); }
...
def(AST_Node, return_false);
def(AST_String, return_true);
def(AST_UnaryPrefix, function(){
return this.operator == "typeof";
});
def(AST_Binary, function(compressor){
return this.operator == "+" &&
(this.left.is_string(compressor) || this.right.is_string(compressor));
});
def(AST_Assign, function(compressor){
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
def(AST_Seq, function(compressor){
return this.cdr.is_string(compressor);
});
...
lift_sequences = function (compressor){ if (compressor.option("sequences")) { if (this.left instanceof AST_Seq) { var seq = this.left; var x = seq.to_array(); var e = this.clone(); e.left = x.pop(); x.push(e); return AST_Seq.from_array(x).optimize(compressor); } if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) { var assign = this.operator == "=" && this.left instanceof AST_SymbolRef; var root = this.right.clone(); var cursor, seq = root; while (assign || !seq.car.has_side_effects(compressor)) { cursor = seq; if (seq.cdr instanceof AST_Seq) { seq = seq.cdr = seq.cdr.clone(); } else break; } if (cursor) { var e = this.clone(); e.right = cursor.cdr; cursor.cdr = e; return root.optimize(compressor); } } } return this; }
...
return seq;
}
}
return this;
});
OPT(AST_UnaryPostfix, function(self, compressor){
return self.lift_sequences(compressor);
});
OPT(AST_UnaryPrefix, function(self, compressor){
var e = self.expression;
if (self.operator == "delete"
&& !(e instanceof AST_SymbolRef
|| e instanceof AST_PropAccess
...
needs_parens = function (output){ var p = output.parent(); // (foo && bar)() if (p instanceof AST_Call && p.expression === this) return true; // typeof (foo && bar) if (p instanceof AST_Unary) return true; // (foo && bar)["prop"], (foo && bar).prop if (p instanceof AST_PropAccess && p.expression === this) return true; // this deals with precedence: 3 * (2 + 1) if (p instanceof AST_Binary) { var po = p.operator, pp = PRECEDENCE[po]; var so = this.operator, sp = PRECEDENCE[so]; if (pp > sp || (pp == sp && this === p.right)) { return true; } } }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
negate = function (compressor, first_in_statement){ return func.call(this, compressor, first_in_statement); }
...
continue loop;
}
//---
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
if (!stat.body.value && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array(stat.alternative).concat(ret);
var funs = extract_functions_from_statement_array(body);
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = null;
ret = funs.concat([ stat.transform(compressor) ]);
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_BlockStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Scope(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props. cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;}}
n/a
function AST_Switch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props.expression;}}
n/a
function AST_SwitchBranch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Try(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.bfinally = props .bfinally;this.bcatch = props.bcatch;}}
n/a
function AST_Catch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.argname = props .argname;}}
n/a
function AST_Finally(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_walk = function (visitor) { return visitor._visit(this, function(){ walk_body(this, visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
has_side_effects = function (compressor){ return any(this.body, compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_BlockStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_BlockStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ print_bracketed(self.body, output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function block_aborts(){ var n = this.body.length; return n > 0 && aborts(this.body[n - 1]); }
...
});
})(function(node, func){
node.DEFMETHOD("has_side_effects", func);
});
// tell me if a statement aborts
function aborts(thing) {
return thing && thing.aborts();
};
(function(def){
def(AST_Statement, return_null);
def(AST_Jump, return_this);
function block_aborts(){
var n = this.body.length;
return n > 0 && aborts(this.body[n - 1]);
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Boolean(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Atom(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_False(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_True(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Boolean(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Break(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
function AST_LoopControl(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Break(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self._do_print(output, "break"); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Call(props){ if (props) { this.end = props.end;this.start = props.start;this.args = props.args;this.expression = props .expression;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_New(props){ if (props) { this.end = props.end;this.start = props.start;this.args = props.args;this.expression = props .expression;}}
n/a
function AST_Call(props){ if (props) { this.end = props.end;this.start = props.start;this.args = props.args;this.expression = props .expression;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self.expression.print(output); if (self instanceof AST_New && !need_constructor_parens(self, output)) return; output.with_parens(function(){ self.args.forEach(function(expr, i){ if (i) output.comma(); expr.print(output); }); }); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); var args = this.args; for (var i = 0, len = args.length; i < len; i++) { args[i]._walk(visitor); } }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
drop_side_effect_free = function (compressor, first_in_statement){ if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) { if (this.expression instanceof AST_Function && (!this.expression.name || !this.expression.name.definition().references.length)) { var node = this.clone(); node.expression = node.expression.process_expression(false, compressor); return node; } return this; } if (this.pure) { compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start); this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' '); } var args = trim(this.args, compressor, first_in_statement); return args && AST_Seq.from_array(args); }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
has_pure_annotation = function (compressor) { if (!compressor.option("side_effects")) return false; if (this.pure !== undefined) return this.pure; var pure = false; var comments, last_comment; if (this.start && (comments = this.start.comments_before) && comments.length && /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) { pure = last_comment; } return this.pure = pure; }
...
def(AST_Node, return_true);
def(AST_EmptyStatement, return_false);
def(AST_Constant, return_false);
def(AST_This, return_false);
def(AST_Call, function(compressor){
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this
)) return true;
for (var i = this.args.length; --i >= 0;) {
if (this.args[i].has_side_effects(compressor))
return true;
}
return false;
});
...
has_side_effects = function (compressor){ if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return true; for (var i = this.args.length; --i >= 0;) { if (this.args[i].has_side_effects(compressor)) return true; } return false; }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
needs_parens = function (output){ var p = output.parent(), p1; if (p instanceof AST_New && p.expression === this) return true; // workaround for Safari bug. // https://bugs.webkit.org/show_bug.cgi?id=123506 return this.expression instanceof AST_Function && p instanceof AST_PropAccess && p.expression === this && (p1 = output.parent(1)) instanceof AST_Assign && p1.left === p; }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Case(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props .expression;}}
n/a
function AST_SwitchBranch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Case(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props .expression;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("case"); output.space(); self.expression.print(output); output.print(":"); self._do_print_body(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); walk_body(this, visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
has_side_effects = function (compressor){ return this.expression.has_side_effects(compressor) || any(this.body, compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Catch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.argname = props .argname;}}
n/a
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Catch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.argname = props .argname;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("catch"); output.space(); output.with_parens(function(){ self.argname.print(output); }); output.space(); print_bracketed(self.body, output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.argname._walk(visitor); walk_body(this, visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Conditional(props){ if (props) { this.end = props.end;this.start = props.start;this.alternative = props.alternative ;this.consequent = props.consequent;this.condition = props.condition;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Conditional(props){ if (props) { this.end = props.end;this.start = props.start;this.alternative = props.alternative ;this.consequent = props.consequent;this.condition = props.condition;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self.condition.print(output); output.space(); output.print("?"); output.space(); self.consequent.print(output); output.space(); output.colon(); self.alternative.print(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_eq_null = function (pure_getters) { return this.consequent._eq_null(pure_getters) || this.alternative._eq_null(pure_getters); }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
_eval = function (compressor){ return ev(this.condition, compressor) ? ev(this.consequent, compressor) : ev(this.alternative, compressor); }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.condition._walk(visitor); this.consequent._walk(visitor); this.alternative._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
drop_side_effect_free = function (compressor){ var consequent = this.consequent.drop_side_effect_free(compressor); var alternative = this.alternative.drop_side_effect_free(compressor); if (consequent === this.consequent && alternative === this.alternative) return this; if (!consequent) return alternative ? make_node(AST_Binary, this, { operator: "||", left: this.condition, right: alternative }) : this.condition.drop_side_effect_free(compressor); if (!alternative) return make_node(AST_Binary, this, { operator: "&&", left: this.condition, right: consequent }); var node = this.clone(); node.consequent = consequent; node.alternative = alternative; return node; }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
has_side_effects = function (compressor){ return this.condition.has_side_effects(compressor) || this.consequent.has_side_effects(compressor) || this.alternative.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
is_boolean = function (){ return this.consequent.is_boolean() && this.alternative.is_boolean(); }
...
def(AST_Node, return_false);
def(AST_UnaryPrefix, function(){
return member(this.operator, unary_bool);
});
def(AST_Binary, function(){
return member(this.operator, binary_bool) ||
( (this.operator == "&&" || this.operator == "||") &&
this.left.is_boolean() && this.right.is_boolean() );
});
def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_boolean();
});
...
is_number = function (compressor){ return this.consequent.is_number(compressor) && this.alternative.is_number(compressor); }
...
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function(){
return unary(this.operator);
});
var binary = makePredicate("- * / % & | ^ << >> >>>");
def(AST_Binary, function(compressor){
return binary(this.operator) || this.operator == "+"
&& this.left.is_number(compressor)
&& this.right.is_number(compressor);
});
def(AST_Assign, function(compressor){
return binary(this.operator.slice(0, -1))
|| this.operator == "=" && this.right.is_number(compressor);
});
def(AST_Seq, function(compressor){
...
is_string = function (compressor){ return this.consequent.is_string(compressor) && this.alternative.is_string(compressor); }
...
def(AST_Node, return_false);
def(AST_String, return_true);
def(AST_UnaryPrefix, function(){
return this.operator == "typeof";
});
def(AST_Binary, function(compressor){
return this.operator == "+" &&
(this.left.is_string(compressor) || this.right.is_string(compressor));
});
def(AST_Assign, function(compressor){
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
def(AST_Seq, function(compressor){
return this.cdr.is_string(compressor);
});
...
needs_parens = function (output){ var p = output.parent(); // !(a = false) → true if (p instanceof AST_Unary) return true; // 1 + (a = 2) + 3 → 6, side effect setting a = 2 if (p instanceof AST_Binary && !(p instanceof AST_Assign)) return true; // (a = func)() —or— new (a = Object)() if (p instanceof AST_Call && p.expression === this) return true; // (a = foo) ? bar : baz if (p instanceof AST_Conditional && p.condition === this) return true; // (a = foo)["prop"] —or— (a = foo).prop if (p instanceof AST_PropAccess && p.expression === this) return true; }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
negate = function (compressor, first_in_statement){ return func.call(this, compressor, first_in_statement); }
...
continue loop;
}
//---
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
if (!stat.body.value && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array(stat.alternative).concat(ret);
var funs = extract_functions_from_statement_array(body);
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = null;
ret = funs.concat([ stat.transform(compressor) ]);
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Const(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions;}}
n/a
function AST_Definitions(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions ;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Const(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self._do_print(output, "const"); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function AST_Constant(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_String(props){ if (props) { this.end = props.end;this.start = props.start;this.quote = props.quote;this.value = props .value;}}
n/a
function AST_Number(props){ if (props) { this.end = props.end;this.start = props.start;this.literal = props.literal;this.value = props.value;}}
n/a
function AST_RegExp(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_Atom(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Constant(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print(self.getValue()); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function return_false() { return false; }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
_eval = function (){ return this.getValue(); }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
function return_null() { return null; }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
getValue = function () { return this.value; }
...
return node._eval(compressor);
};
def(AST_Node, function(){
throw def; // not constant
});
def(AST_Constant, function(){
return this.getValue();
});
def(AST_Array, function(compressor){
if (compressor.option("unsafe")) {
return this.elements.map(function(element) {
return ev(element, compressor);
});
}
...
function return_false() { return false; }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Continue(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
function AST_LoopControl(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Continue(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self._do_print(output, "continue"); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_DWLoop(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
function AST_IterationStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Do(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
function AST_While(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
function AST_DWLoop(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Debugger(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Debugger(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("debugger"); output.semicolon(); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Default(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_SwitchBranch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Default(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("default:"); self._do_print_body(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function AST_Definitions(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions ;}}
n/a
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Var(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions;}}
n/a
function AST_Const(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions;}}
n/a
function AST_Definitions(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions ;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_do_print = function (output, kind){ output.print(kind); output.space(); this.definitions.forEach(function(def, i){ if (i) output.comma(); def.print(output); }); var p = output.parent(); var in_for = p instanceof AST_For || p instanceof AST_ForIn; var avoid_semicolon = in_for && p.init === this; if (!avoid_semicolon) output.semicolon(); }
...
arg.print(output);
});
});
output.space();
print_bracketed(self.body, output, true);
});
DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output);
});
/* -----[ exits ]----- */
AST_Exit.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
if (this.value) {
output.space();
...
_walk = function (visitor) { return visitor._visit(this, function(){ var definitions = this.definitions; for (var i = 0, len = definitions.length; i < len; i++) { definitions[i]._walk(visitor); } }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
remove_initializers = function (){ this.definitions.forEach(function(def){ def.value = null }); }
...
function extract_declarations_from_unreachable_code(compressor, stat, target) {
if (!(stat instanceof AST_Defun)) {
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
}
stat.walk(new TreeWalker(function(node){
if (node instanceof AST_Definitions) {
compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
node.remove_initializers();
target.push(node);
return true;
}
if (node instanceof AST_Defun) {
target.push(node);
return true;
}
...
to_assignments = function (compressor){ var reduce_vars = compressor.option("reduce_vars"); var assignments = this.definitions.reduce(function(a, def){ if (def.value) { var name = make_node(AST_SymbolRef, def.name, def.name); a.push(make_node(AST_Assign, def, { operator : "=", left : name, right : def.value })); if (reduce_vars) name.definition().fixed = false; } return a; }, []); if (assignments.length == 0) return null; return AST_Seq.from_array(assignments); }
...
return make_node(AST_EmptyStatement, node);
}
if (node instanceof AST_Var && hoist_vars) {
node.definitions.forEach(function(def){
vars.set(def.name.name, def);
++vars_found;
});
var seq = node.to_assignments(compressor);
var p = tt.parent();
if (p instanceof AST_ForIn && p.init === node) {
if (seq == null) {
var def = node.definitions[0].name;
return make_node(AST_SymbolRef, def, def);
}
return seq;
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Defun(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props. cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Lambda(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Defun(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props. cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function return_true() { return true; }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Directive(props){ if (props) { this.end = props.end;this.start = props.start;this.quote = props.quote;this.scope = props.scope;this.value = props.value;}}
n/a
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Directive(props){ if (props) { this.end = props.end;this.start = props.start;this.quote = props.quote;this.scope = props.scope;this.value = props.value;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print_string(self.value, self.quote); output.semicolon(); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Do(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
function AST_DWLoop(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Do(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("do"); output.space(); make_block(self.body, output); output.space(); output.print("while"); output.space(); output.with_parens(function(){ self.condition.print(output); }); output.semicolon(); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.body._walk(visitor); this.condition._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Dot(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
function AST_PropAccess(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Dot(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ var expr = self.expression; expr.print(output); if (expr instanceof AST_Number && expr.getValue() >= 0) { if (!/[xa-f.)]/i.test(output.last())) { output.print("."); } } output.print("."); // the name after dot would be mapped about here. output.add_mapping(self.end); output.print_name(self.property); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_find_defs = function (compressor, suffix){ return this.expression._find_defs(compressor, "." + this.property + suffix); }
...
if (parent instanceof AST_Unary && unary_side_effects(parent.operator)) return parent.expression;
if (parent instanceof AST_Assign && parent.left === node) return node;
}
(function (def){
AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
if (!compressor.option("global_defs")) return;
var def = this._find_defs(compressor, "");
if (def) {
var node, parent = this, level = 0;
do {
node = parent;
parent = compressor.parent(level++);
} while (parent instanceof AST_PropAccess && parent.expression === node);
if (is_lhs(node, parent)) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
drop_side_effect_free = function (compressor, first_in_statement){ if (this.expression.may_eq_null(compressor)) return this; return this.expression.drop_side_effect_free(compressor, first_in_statement); }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
has_side_effects = function (compressor){ return this.expression.may_eq_null(compressor) || this.expression.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_EmptyStatement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_EmptyStatement(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.semicolon(); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
function return_false() { return false; }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Exit(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_Jump(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Return(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_Throw(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_Exit(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_do_print = function (output, kind){ output.print(kind); if (this.value) { output.space(); this.value.print(output); } output.semicolon(); }
...
arg.print(output);
});
});
output.space();
print_bracketed(self.body, output, true);
});
DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output);
});
/* -----[ exits ]----- */
AST_Exit.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
if (this.value) {
output.space();
...
_walk = function (visitor) { return visitor._visit(this, this.value && function(){ this.value._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_False(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Boolean(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_False(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function return_true() { return true; }
...
def(AST_Node, return_false);
def(AST_UnaryPrefix, function(){
return member(this.operator, unary_bool);
});
def(AST_Binary, function(){
return member(this.operator, binary_bool) ||
( (this.operator == "&&" || this.operator == "||") &&
this.left.is_boolean() && this.right.is_boolean() );
});
def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_boolean();
});
...
function AST_Finally(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Finally(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("finally"); output.space(); print_bracketed(self.body, output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
function AST_For(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.step = props.step ;this.condition = props.condition;this.init = props.init;}}
n/a
function AST_IterationStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_For(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.step = props.step ;this.condition = props.condition;this.init = props.init;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("for"); output.space(); output.with_parens(function(){ if (self.init) { if (self.init instanceof AST_Definitions) { self.init.print(output); } else { parenthesize_for_noin(self.init, output, true); } output.print(";"); output.space(); } else { output.print(";"); } if (self.condition) { self.condition.print(output); output.print(";"); output.space(); } else { output.print(";"); } if (self.step) { self.step.print(output); } }); output.space(); self._do_print_body(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ if (this.init) this.init._walk(visitor); if (this.condition) this.condition._walk(visitor); if (this.step) this.step._walk(visitor); this.body._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_ForIn(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.object = props .object;this.name = props.name;this.init = props.init;}}
n/a
function AST_IterationStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_ForIn(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.object = props .object;this.name = props.name;this.init = props.init;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("for"); output.space(); output.with_parens(function(){ self.init.print(output); output.space(); output.print("in"); output.space(); self.object.print(output); }); output.space(); self._do_print_body(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.init._walk(visitor); this.object._walk(visitor); this.body._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Function(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Lambda(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Function(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function return_false() { return false; }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
function return_null() { return null; }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
function return_false() { return false; }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
needs_parens = function (output){ if (first_in_statement(output)) { return true; } if (output.option('wrap_iife')) { var p = output.parent(); return p instanceof AST_Call && p.expression === this; } return false; }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
negate = function (compressor, first_in_statement){ return func.call(this, compressor, first_in_statement); }
...
continue loop;
}
//---
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
if (!stat.body.value && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array(stat.alternative).concat(ret);
var funs = extract_functions_from_statement_array(body);
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = null;
ret = funs.concat([ stat.transform(compressor) ]);
...
next_mangled = function (options, def){ // #179, #326 // in Safari strict mode, something like (function x(x){...}) is a syntax error; // a function expression's argument cannot shadow the function expression's name var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition(); // the function's mangled_name is null when keep_fnames is true var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null; while (true) { var name = AST_Lambda.prototype.next_mangled.call(this, options, def); if (!tricky_name || tricky_name != name) return name; } }
...
var sym = this.orig[0];
if (!options.screw_ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
this.mangled_name = def.mangled_name || def.name;
} else
this.mangled_name = s.next_mangled(options, this);
if (this.global && cache) {
cache.set(this.name, this.mangled_name);
}
}
}
};
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Hole(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Atom(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Hole(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function noop() {}
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function To_Moz_ArrayHole() { return null }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_If(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.alternative = props .alternative;this.condition = props.condition;}}
n/a
function AST_StatementWithBody(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_If(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.alternative = props .alternative;this.condition = props.condition;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("if"); output.space(); output.with_parens(function(){ self.condition.print(output); }); output.space(); if (self.alternative) { make_then(self, output); output.space(); output.print("else"); output.space(); if (self.alternative instanceof AST_If) self.alternative.print(output); else force_statement(self.alternative, output); } else { self._do_print_body(output); } }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.condition._walk(visitor); this.body._walk(visitor); if (this.alternative) this.alternative._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
aborts = function (){ return this.alternative && aborts(this.body) && aborts(this.alternative) && this; }
...
});
})(function(node, func){
node.DEFMETHOD("has_side_effects", func);
});
// tell me if a statement aborts
function aborts(thing) {
return thing && thing.aborts();
};
(function(def){
def(AST_Statement, return_null);
def(AST_Jump, return_this);
function block_aborts(){
var n = this.body.length;
return n > 0 && aborts(this.body[n - 1]);
...
has_side_effects = function (compressor){ return this.condition.has_side_effects(compressor) || this.body && this.body.has_side_effects(compressor) || this.alternative && this.alternative.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Infinity(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Atom(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Infinity(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
function AST_IterationStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_StatementWithBody(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_DWLoop(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
function AST_For(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.step = props.step ;this.condition = props.condition;this.init = props.init;}}
n/a
function AST_ForIn(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.object = props .object;this.name = props.name;this.init = props.init;}}
n/a
function AST_IterationStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function AST_Jump(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Exit(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_LoopControl(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
function AST_Jump(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function return_this() { return this; }
...
});
})(function(node, func){
node.DEFMETHOD("has_side_effects", func);
});
// tell me if a statement aborts
function aborts(thing) {
return thing && thing.aborts();
};
(function(def){
def(AST_Statement, return_null);
def(AST_Jump, return_this);
function block_aborts(){
var n = this.body.length;
return n > 0 && aborts(this.body[n - 1]);
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
function AST_Label(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;this.references = props.references;this.initialize();}}
n/a
function AST_Symbol(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Label(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;this.references = props.references;this.initialize();}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
initialize = function () { this.references = []; this.thedef = this; }
...
props = props.concat(base.PROPS);
var code = "return function AST_" + type + "(props){ if (props) { ";
for (var i = props.length; --i >= 0;) {
code += "this." + props[i] + " = props." + props[i] + ";";
}
var proto = base && new base;
if (proto && proto.initialize || (methods && methods.initialize))
code += "this.initialize();";
code += "}}";
var ctor = new Function(code)();
if (proto) {
ctor.prototype = proto;
ctor.BASE = base;
}
if (base) base.SUBCLASSES.push(ctor);
...
undeclared = function (){ return false; }
...
|| this.alternative.has_side_effects(compressor);
});
def(AST_Unary, function(compressor){
return unary_side_effects(this.operator)
|| this.expression.has_side_effects(compressor);
});
def(AST_SymbolRef, function(compressor){
return this.undeclared();
});
def(AST_Object, function(compressor){
return any(this.properties, compressor);
});
def(AST_ObjectProperty, function(compressor){
return this.value.has_side_effects(compressor);
});
...
unmangleable = function (){ return false; }
...
|| this.orig[0] instanceof AST_SymbolDefun));
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
if (this.global && cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
}
else if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope;
var sym = this.orig[0];
if (!options.screw_ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
this.mangled_name = def.mangled_name || def.name;
...
function AST_LabelRef(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
n/a
function AST_Symbol(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_LabelRef(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
undeclared = function (){ return false; }
...
|| this.alternative.has_side_effects(compressor);
});
def(AST_Unary, function(compressor){
return unary_side_effects(this.operator)
|| this.expression.has_side_effects(compressor);
});
def(AST_SymbolRef, function(compressor){
return this.undeclared();
});
def(AST_Object, function(compressor){
return any(this.properties, compressor);
});
def(AST_ObjectProperty, function(compressor){
return this.value.has_side_effects(compressor);
});
...
function AST_LabeledStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.label = props.label;}}
n/a
function AST_StatementWithBody(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_LabeledStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.label = props.label;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self.label.print(output); output.colon(); self.body.print(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.label._walk(visitor); this.body._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
clone = function (deep) { var node = this._clone(deep); if (deep) { var refs = node.label.references; var label = this.label; node.walk(new TreeWalker(function(node) { if (node instanceof AST_LoopControl && node.label && node.label.thedef === label) { refs.push(node); } })); } return node; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
...
has_side_effects = function (compressor){ return this.body.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Lambda(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Scope(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props. cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Accessor(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Function(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Defun(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props. cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Lambda(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self._do_print(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_do_print = function (output, nokeyword){ var self = this; if (!nokeyword) { output.print("function"); } if (self.name) { output.space(); self.name.print(output); } output.with_parens(function(){ self.argnames.forEach(function(arg, i){ if (i) output.comma(); arg.print(output); }); }); output.space(); print_bracketed(self.body, output, true); }
...
arg.print(output);
});
});
output.space();
print_bracketed(self.body, output, true);
});
DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output);
});
/* -----[ exits ]----- */
AST_Exit.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
if (this.value) {
output.space();
...
_eval = function (){ throw def; }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
_walk = function (visitor) { return visitor._visit(this, function(){ if (this.name) this.name._walk(visitor); var argnames = this.argnames; for (var i = 0, len = argnames.length; i < len; i++) { argnames[i]._walk(visitor); } walk_body(this, visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
init_scope_vars = function (){ AST_Scope.prototype.init_scope_vars.apply(this, arguments); this.uses_arguments = false; this.def_variable(new AST_SymbolVar({ name: "arguments", start: this.start, end: this.end })); }
...
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Catch) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(save_scope);
descend();
scope = save_scope;
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_LoopControl(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
function AST_Jump(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Break(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
function AST_Continue(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
n/a
function AST_LoopControl(props){ if (props) { this.end = props.end;this.start = props.start;this.label = props.label;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_do_print = function (output, kind){ output.print(kind); if (this.label) { output.space(); this.label.print(output); } output.semicolon(); }
...
arg.print(output);
});
});
output.space();
print_bracketed(self.body, output, true);
});
DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output);
});
/* -----[ exits ]----- */
AST_Exit.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
if (this.value) {
output.space();
...
_walk = function (visitor) { return visitor._visit(this, this.label && function(){ this.label._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_NaN(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Atom(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_NaN(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
function AST_New(props){ if (props) { this.end = props.end;this.start = props.start;this.args = props.args;this.expression = props .expression;}}
n/a
function AST_Call(props){ if (props) { this.end = props.end;this.start = props.start;this.args = props.args;this.expression = props .expression;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_New(props){ if (props) { this.end = props.end;this.start = props.start;this.args = props.args;this.expression = props .expression;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("new"); output.space(); AST_Call.prototype._codegen(self, output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
needs_parens = function (output){ var p = output.parent(); if (!need_constructor_parens(this, output) && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]() || p instanceof AST_Call && p.expression === this)) // (new foo)(bar) return true; }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
from_mozilla_ast = function (node){ var save_stack = FROM_MOZ_STACK; FROM_MOZ_STACK = []; var ast = from_moz(node); FROM_MOZ_STACK = save_stack; return ast; }
...
JavaScript ASTs in SpiderMonkey format.
Example:
```javascript
function uglify(ast, options, mangle) {
// Conversion from SpiderMonkey AST to internal format
var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
// Compression
uAST.figure_out_scope();
uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional)
if (mangle) {
...
warn = function (txt, props) { if (AST_Node.warn_function) AST_Node.warn_function(string_template(txt, props)); }
...
stat.definitions.forEach(function(def) {
if (def.value && def.value instanceof AST_Lambda) {
a.push(make_injector(def.value, def.name));
}
});
}
else {
compressor.warn("Unknown statement marked with @ngInject [{file
}:{line},{col}]", token);
}
}
}
}
return a;
}, []);
...
warn_function = function (txt) { console.error("WARN: %s", txt); }
...
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
};
/* -----[ statements ]----- */
var AST_Statement = DEFNODE("Statement", null, {
$documentation: "Base class of all statements",
});
...
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_VarDef(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.name = props .name;}}
n/a
function AST_ObjectProperty(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
function AST_Symbol(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
function AST_Constant(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Call(props){ if (props) { this.end = props.end;this.start = props.start;this.args = props.args;this.expression = props .expression;}}
n/a
function AST_Seq(props){ if (props) { this.end = props.end;this.start = props.start;this.cdr = props.cdr;this.car = props.car;}}
n/a
function AST_PropAccess(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
function AST_Unary(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression;this.operator = props.operator;}}
n/a
function AST_Binary(props){ if (props) { this.end = props.end;this.start = props.start;this.right = props.right;this.operator = props.operator;this.left = props.left;}}
n/a
function AST_Conditional(props){ if (props) { this.end = props.end;this.start = props.start;this.alternative = props.alternative ;this.consequent = props.consequent;this.condition = props.condition;}}
n/a
function AST_Array(props){ if (props) { this.end = props.end;this.start = props.start;this.elements = props.elements;}}
n/a
function AST_Object(props){ if (props) { this.end = props.end;this.start = props.start;this.properties = props.properties;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_clone = function (deep) { if (deep) { var self = this.clone(); return self.transform(new TreeTransformer(function(node) { if (node !== self) { return node.clone(true); } })); } return new this.CTOR(this); }
...
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
...
function is_strict(pure_getters) { return /strict/.test(pure_getters); }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
_eval = function (){ throw def; // not constant }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
function noop() {}
...
if (parent instanceof AST_Unary && unary_side_effects(parent.operator)) return parent.expression;
if (parent instanceof AST_Assign && parent.left === node) return node;
}
(function (def){
AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
if (!compressor.option("global_defs")) return;
var def = this._find_defs(compressor, "");
if (def) {
var node, parent = this, level = 0;
do {
node = parent;
parent = compressor.parent(level++);
} while (parent instanceof AST_PropAccess && parent.expression === node);
if (is_lhs(node, parent)) {
...
_walk = function (visitor) { return visitor._visit(this); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
add_comments = function (output){ if (output._readonly) return; var self = this; var start = self.start; if (start && !start._comments_dumped) { start._comments_dumped = true; var comments = start.comments_before || []; // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112 // and https://github.com/mishoo/UglifyJS2/issues/372 if (self instanceof AST_Exit && self.value) { self.value.walk(new TreeWalker(function(node){ if (node.start && node.start.comments_before) { comments = comments.concat(node.start.comments_before); node.start.comments_before = []; } if (node instanceof AST_Function || node instanceof AST_Array || node instanceof AST_Object) { return true; // don't go inside. } })); } if (output.pos() == 0) { if (comments.length > 0 && output.option("shebang") && comments[0].type == "comment5") { output.print("#!" + comments.shift().value + "\n"); output.indent(); } var preamble = output.option("preamble"); if (preamble) { output.print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n")); } } comments = comments.filter(output.comment_filter, self); // Keep single line comments after nlb, after nlb if (!output.option("beautify") && comments.length > 0 && /comment[134]/.test(comments[0].type) && output.col() !== 0 && comments[0].nlb) { output.print("\n"); } comments.forEach(function(c){ if (/comment[134]/.test(c.type)) { output.print("//" + c.value + "\n"); output.indent(); } else if (c.type == "comment2") { output.print("/*" + c.value + "*/"); if (start.nlb) { output.print("\n"); output.indent(); } else { output.space(); } } }); } }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
clone = function (deep) { return this._clone(deep); }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
...
constant_value = function (compressor){ // Accomodate when option evaluate=false. if (this instanceof AST_Constant && !(this instanceof AST_RegExp)) { return this.value; } // Accomodate the common constant expressions !0 and -1 when option evaluate=false. if (this instanceof AST_UnaryPrefix && this.expression instanceof AST_Constant) switch (this.operator) { case "!": return !this.expression.value; case "~": return ~this.expression.value; case "-": return -this.expression.value; case "+": return +this.expression.value; default: throw new Error(string_template("Cannot evaluate unary expression {value}", { value: this.print_to_string() })); } var result = this.evaluate(compressor); if (result !== this) { return result; } throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start)); }
n/a
function return_this() { return this; }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
equivalent_to = function (node){ return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string(); }
...
eliminate_branch(branch, body[body.length - 1]);
continue;
}
}
if (aborts(branch)) {
var prev = body[body.length - 1];
if (aborts(prev) && prev.body.length == branch.body.length
&& make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node
(AST_BlockStatement, branch, branch))) {
prev.body = [];
}
}
body.push(branch);
}
while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
if (body.length > 0) {
...
evaluate = function (compressor){ if (!compressor.option("evaluate")) return this; try { var val = this._eval(compressor); return !val || val instanceof RegExp || typeof val != "object" ? val : this; } catch(ex) { if (ex !== def) throw ex; return this; } }
...
case "+":
return +this.expression.value;
default:
throw new Error(string_template("Cannot evaluate unary expression {value}", {
value: this.print_to_string()
}));
}
var result = this.evaluate(compressor);
if (result !== this) {
return result;
}
throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start));
});
def(AST_Statement, function(){
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
...
function return_true() { return true; }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
function return_false() { return false; }
...
def(AST_Node, return_false);
def(AST_UnaryPrefix, function(){
return member(this.operator, unary_bool);
});
def(AST_Binary, function(){
return member(this.operator, binary_bool) ||
( (this.operator == "&&" || this.operator == "||") &&
this.left.is_boolean() && this.right.is_boolean() );
});
def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_boolean();
});
...
is_constant = function (){ // Accomodate when compress option evaluate=false // as well as the common constant expressions !0 and -1 if (this instanceof AST_Constant) { return !(this instanceof AST_RegExp); } else { return this instanceof AST_UnaryPrefix && this.expression instanceof AST_Constant && unaryPrefix(this.operator); } }
...
}
var ref = def.references[0];
// Don't replace ref if eval() or with statement in scope.
if (ref.scope.uses_eval || ref.scope.uses_with) break;
// Constant single use vars can be replaced in any scope.
if (var_decl.value.is_constant()) {
var ctt = new TreeTransformer(function(node) {
var parent = ctt.parent();
if (parent instanceof AST_IterationStatement
&& (parent.condition === node || parent.init === node)) {
return node;
}
if (node === ref)
...
function return_false() { return false; }
...
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function(){
return unary(this.operator);
});
var binary = makePredicate("- * / % & | ^ << >> >>>");
def(AST_Binary, function(compressor){
return binary(this.operator) || this.operator == "+"
&& this.left.is_number(compressor)
&& this.right.is_number(compressor);
});
def(AST_Assign, function(compressor){
return binary(this.operator.slice(0, -1))
|| this.operator == "=" && this.right.is_number(compressor);
});
def(AST_Seq, function(compressor){
...
function return_false() { return false; }
...
def(AST_Node, return_false);
def(AST_String, return_true);
def(AST_UnaryPrefix, function(){
return this.operator == "typeof";
});
def(AST_Binary, function(compressor){
return this.operator == "+" &&
(this.left.is_string(compressor) || this.right.is_string(compressor));
});
def(AST_Assign, function(compressor){
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
def(AST_Seq, function(compressor){
return this.cdr.is_string(compressor);
});
...
may_eq_null = function (compressor) { var pure_getters = compressor.option("pure_getters"); return !pure_getters || this._eq_null(pure_getters); }
...
def(AST_ObjectProperty, function(compressor){
return this.value.has_side_effects(compressor);
});
def(AST_Array, function(compressor){
return any(this.elements, compressor);
});
def(AST_Dot, function(compressor){
return this.expression.may_eq_null(compressor)
|| this.expression.has_side_effects(compressor);
});
def(AST_Sub, function(compressor){
return this.expression.may_eq_null(compressor)
|| this.expression.has_side_effects(compressor)
|| this.property.has_side_effects(compressor);
});
...
needs_parens = function (){ return false; }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
negate = function (compressor, first_in_statement){ return func.call(this, compressor, first_in_statement); }
...
continue loop;
}
//---
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
if (!stat.body.value && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array(stat.alternative).concat(ret);
var funs = extract_functions_from_statement_array(body);
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = null;
ret = funs.concat([ stat.transform(compressor) ]);
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
print = function (stream, force_parens){ var self = this, generator = self._codegen, prev_use_asm = use_asm; if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope) { use_asm = true; } function doit() { self.add_comments(stream); self.add_source_map(stream); generator(self, stream); } stream.push_node(self); if (force_parens || self.needs_parens(stream)) { stream.with_parens(doit); } else { doit(); } stream.pop_node(); if (self instanceof AST_Scope) { use_asm = prev_use_asm; } }
...
#### Generating output
AST nodes have a `print` method that takes an output stream. Essentially,
to generate code you do this:
```javascript
var stream = UglifyJS.OutputStream(options);
compressed_ast.print(stream);
var code = stream.toString(); // this is your minified code
```
or, for a shortcut you can do:
```javascript
var code = compressed_ast.print_to_string(options);
```
...
print_to_string = function (options){ var s = OutputStream(options); if (!options) s._readonly = true; this.print(s); return s.get(); }
...
var stream = UglifyJS.OutputStream(options);
compressed_ast.print(stream);
var code = stream.toString(); // this is your minified code
```
or, for a shortcut you can do:
```javascript
var code = compressed_ast.print_to_string(options);
```
As usual, `options` is optional. The output stream accepts a lot of options,
most of them documented above in section “Beautifier options”. The two
which we care about here are `source_map` and `comments`.
#### Keeping comments in the output
...
process_expression = function (insert, compressor) { var self = this; var tt = new TreeTransformer(function(node) { if (insert && node instanceof AST_SimpleStatement) { return make_node(AST_Return, node, { value: node.body }); } if (!insert && node instanceof AST_Return) { if (compressor) { var value = node.value && node.value.drop_side_effect_free(compressor, true); return value ? make_node(AST_SimpleStatement, node, { body: value }) : make_node(AST_EmptyStatement, node); } return make_node(AST_SimpleStatement, node, { body: node.value || make_node(AST_UnaryPrefix, node, { operator: "void", expression: make_node(AST_Number, node, { value: 0 }) }) }); } if (node instanceof AST_Lambda && node !== self) { return node; } if (node instanceof AST_Block) { var index = node.body.length - 1; if (index >= 0) { node.body[index] = node.body[index].transform(tt); } } if (node instanceof AST_If) { node.body = node.body.transform(tt); if (node.alternative) { node.alternative = node.alternative.transform(tt); } } if (node instanceof AST_With) { node.body = node.body.transform(tt); } return node; }); return self.transform(tt); }
...
};
Compressor.prototype = new TreeTransformer;
merge(Compressor.prototype, {
option: function(key) { return this.options[key] },
compress: function(node) {
if (this.option("expression")) {
node = node.process_expression(true);
}
var passes = +this.options.passes || 1;
for (var pass = 0; pass < passes && pass < 3; ++pass) {
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this, true);
node = node.transform(this);
}
...
reset_opt_flags = function (compressor, rescan){ var reduce_vars = rescan && compressor.option("reduce_vars"); var toplevel = compressor.option("toplevel"); var safe_ids = Object.create(null); var suppressor = new TreeWalker(function(node) { if (node instanceof AST_Symbol) { var d = node.definition(); if (node instanceof AST_SymbolRef) d.references.push(node); d.fixed = false; } }); var tw = new TreeWalker(function(node, descend){ node._squeezed = false; node._optimized = false; if (reduce_vars) { if (node instanceof AST_Toplevel) node.globals.each(reset_def); if (node instanceof AST_Scope) node.variables.each(reset_def); if (node instanceof AST_SymbolRef) { var d = node.definition(); d.references.push(node); if (d.fixed === undefined || !is_safe(d) || is_modified(node, 0, node.fixed_value() instanceof AST_Lambda)) { d.fixed = false; } } if (node instanceof AST_SymbolCatch) { node.definition().fixed = false; } if (node instanceof AST_VarDef) { var d = node.name.definition(); if (d.fixed == null) { if (node.value) { d.fixed = function() { return node.value; }; mark(d, false); descend(); } else { d.fixed = null; } mark(d, true); return true; } else if (node.value) { d.fixed = false; } } if (node instanceof AST_Defun) { var d = node.name.definition(); if (!toplevel && d.global || is_safe(d)) { d.fixed = false; } else { d.fixed = node; mark(d, true); } var save_ids = safe_ids; safe_ids = Object.create(null); descend(); safe_ids = save_ids; return true; } var iife; if (node instanceof AST_Function && !node.name && (iife = tw.parent()) instanceof AST_Call && iife.expression === node) { // Virtually turn IIFE parameters into variable definitions: // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})() // So existing transformation rules can work on them. node.argnames.forEach(function(arg, i) { var d = arg.definition(); d.fixed = function() { return iife.args[i] || make_node(AST_Undefined, iife); }; mark(d, true); }); } if (node instanceof AST_If || node instanceof AST_DWLoop) { node.condition.walk(tw); push(); node.body.walk(tw); pop(); if (node.alternative) { push(); node.alternative.walk(tw); pop(); } return true; } if (node instanceof AST_LabeledStatement) { push(); node.body.walk(tw); pop(); return true; } if (node instanceof AST_For) { if (node.init) node.init.walk(tw); push(); if (node.condition) node.condition.walk(tw); node.body.walk(tw); if (node.step) node.step.walk(tw); pop(); return true; } if (node instanceof AST_ForIn) { node.init.walk(suppressor); ...
...
compress: function(node) {
if (this.option("expression")) {
node = node.process_expression(true);
}
var passes = +this.options.passes || 1;
for (var pass = 0; pass < passes && pass < 3; ++pass) {
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this, true);
node = node.transform(this);
}
if (this.option("expression")) {
node = node.process_expression(false);
}
return node;
},
...
resolve_defines = function (compressor) { if (!compressor.option("global_defs")) return; var def = this._find_defs(compressor, ""); if (def) { var node, parent = this, level = 0; do { node = parent; parent = compressor.parent(level++); } while (parent instanceof AST_PropAccess && parent.expression === node); if (is_lhs(node, parent)) { compressor.warn('global_defs ' + this.print_to_string() + ' redefined [{file}:{line},{col}]', this.start); } else { return def; } } }
...
ev = make_node_from_constant(ev, self).optimize(compressor);
return best_of(compressor, ev, self);
}
return self;
});
OPT(AST_SymbolRef, function(self, compressor){
var def = self.resolve_defines(compressor);
if (def) {
return def.optimize(compressor);
}
// testing against !self.scope.uses_with first is an optimization
if (compressor.option("screw_ie8")
&& self.undeclared()
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
walk = function (visitor) { return this._walk(visitor); // not sure the indirection will be any help }
...
console.log("%s", JSON.stringify(output, null, 2));
}
function getProps(filename) {
var code = fs.readFileSync(filename, "utf8");
var ast = U2.parse(code);
ast.walk(new U2.TreeWalker(function(node){
if (node instanceof U2.AST_ObjectKeyVal) {
add(node.key);
}
else if (node instanceof U2.AST_ObjectProperty) {
add(node.key.name);
}
else if (node instanceof U2.AST_Dot) {
...
function AST_Null(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Atom(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Null(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function return_true() { return true; }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Number(props){ if (props) { this.end = props.end;this.start = props.start;this.literal = props.literal;this.value = props.value;}}
n/a
function AST_Constant(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Number(props){ if (props) { this.end = props.end;this.start = props.start;this.literal = props.literal;this.value = props.value;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ if (use_asm && self.start && self.start.raw != null) { output.print(self.start.raw); } else { output.print(make_num(self.getValue())); } }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function return_true() { return true; }
...
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function(){
return unary(this.operator);
});
var binary = makePredicate("- * / % & | ^ << >> >>>");
def(AST_Binary, function(compressor){
return binary(this.operator) || this.operator == "+"
&& this.left.is_number(compressor)
&& this.right.is_number(compressor);
});
def(AST_Assign, function(compressor){
return binary(this.operator.slice(0, -1))
|| this.operator == "=" && this.right.is_number(compressor);
});
def(AST_Seq, function(compressor){
...
needs_parens = function (output){ var p = output.parent(); if (p instanceof AST_PropAccess && p.expression === this) { var value = this.getValue(); if (value < 0 || /^0/.test(make_num(value))) { return true; } } }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
function AST_Object(props){ if (props) { this.end = props.end;this.start = props.start;this.properties = props.properties;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Object(props){ if (props) { this.end = props.end;this.start = props.start;this.properties = props.properties;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ if (self.properties.length > 0) output.with_block(function(){ self.properties.forEach(function(prop, i){ if (i) { output.print(","); output.newline(); } output.indent(); prop.print(output); }); output.newline(); }); else output.print("{}"); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function return_false() { return false; }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
_eval = function (compressor){ if (compressor.option("unsafe")) { var val = {}; for (var i = 0, len = this.properties.length; i < len; i++) { var prop = this.properties[i]; var key = prop.key; if (key instanceof AST_Symbol) { key = key.name; } else if (key instanceof AST_Node) { key = ev(key, compressor); } if (typeof Object.prototype[key] === 'function') { throw def; } val[key] = ev(prop.value, compressor); } return val; } throw def; }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
_walk = function (visitor) { return visitor._visit(this, function(){ var properties = this.properties; for (var i = 0, len = properties.length; i < len; i++) { properties[i]._walk(visitor); } }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
drop_side_effect_free = function (compressor, first_in_statement){ var values = trim(this.properties, compressor, first_in_statement); return values && AST_Seq.from_array(values); }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
has_side_effects = function (compressor){ return any(this.properties, compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
needs_parens = function (output){ return first_in_statement(output); }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_ObjectGetter(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
function AST_ObjectProperty(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_ObjectGetter(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("get"); output.space(); self.key.print(output); self.value._do_print(output, true); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
function AST_ObjectKeyVal(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;this.quote = props.quote;}}
n/a
function AST_ObjectProperty(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_ObjectKeyVal(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;this.quote = props.quote;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ var key = self.key; var quote = self.quote; if (output.option("quote_keys")) { output.print_string(key + ""); } else if ((typeof key == "number" || !output.option("beautify") && +key + "" == key) && parseFloat(key) >= 0) { output.print(make_num(key)); } else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) { if (quote && output.option("keep_quoted_props")) { output.print_string(key, quote); } else { output.print_name(key); } } else { output.print_string(key, quote); } output.colon(); self.value.print(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function AST_ObjectProperty(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_ObjectKeyVal(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;this.quote = props.quote;}}
n/a
function AST_ObjectSetter(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
function AST_ObjectGetter(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
function AST_ObjectProperty(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.value._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
drop_side_effect_free = function (compressor, first_in_statement){ return this.value.drop_side_effect_free(compressor, first_in_statement); }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
has_side_effects = function (compressor){ return this.value.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_ObjectSetter(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
function AST_ObjectProperty(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_ObjectSetter(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.key = props.key;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("set"); output.space(); self.key.print(output); self.value._do_print(output, true); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
function AST_PropAccess(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Dot(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
function AST_Sub(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
function AST_PropAccess(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_eval = function (compressor){ if (compressor.option("unsafe")) { var key = this.property; if (key instanceof AST_Node) { key = ev(key, compressor); } var val = ev(this.expression, compressor); if (val && HOP(val, key)) { return val[key]; } } throw def; }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
needs_parens = function (output){ var p = output.parent(); if (p instanceof AST_New && p.expression === this) { // i.e. new (foo.bar().baz) // // if there's one call into this subtree, then we need // parens around it too, otherwise the call will be // interpreted as passing the arguments to the upper New // expression. try { this.walk(new TreeWalker(function(node){ if (node instanceof AST_Call) throw p; })); } catch(ex) { if (ex !== p) throw ex; return true; } } }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_RegExp(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_Constant(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_RegExp(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ var str = self.getValue().toString(); if (output.option("ascii_only")) { str = output.to_ascii(str); } else if (output.option("unescape_regexps")) { str = str.split("\\\\").map(function(str){ return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){ var code = parseInt(s.substr(2), 16); return regexp_safe_literal(code) ? String.fromCharCode(code) : s; }); }).join("\\\\"); } output.print(str); var p = output.parent(); if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self) output.print(" "); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Return(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_Exit(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Return(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self._do_print(output, "return"); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Scope(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props. cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;}}
n/a
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Toplevel(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.globals = props .globals;}}
n/a
function AST_Lambda(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.uses_arguments = props.uses_arguments;this.argnames = props.argnames;this.name = props.name;}}
n/a
function AST_Scope(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props. cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
def_function = function (symbol){ this.functions.set(symbol.name, this.def_variable(symbol)); }
...
node.scope = scope;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
// the parent scope. The reason is that we enter a new
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
...
def_variable = function (symbol){ var def; if (!this.variables.has(symbol.name)) { def = new SymbolDef(this, this.variables.size(), symbol); this.variables.set(symbol.name, def); def.global = !this.parent_scope; } else { def = this.variables.get(symbol.name); def.orig.push(symbol); } return symbol.thedef = def; }
...
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
(node.scope = defun.parent_scope).def_function(node);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) {
defun.def_variable(node);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
...
drop_unused = function (compressor){ var self = this; if (compressor.has_directive("use asm")) return self; var toplevel = compressor.option("toplevel"); if (compressor.option("unused") && (!(self instanceof AST_Toplevel) || toplevel) && !self.uses_eval && !self.uses_with) { var assign_as_unused = !/keep_assign/.test(compressor.option("unused")); var drop_funcs = /funcs/.test(toplevel); var drop_vars = /vars/.test(toplevel); if (!(self instanceof AST_Toplevel) || toplevel == true) { drop_funcs = drop_vars = true; } var in_use = []; var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use if (self instanceof AST_Toplevel && compressor.top_retain) { self.variables.each(function(def) { if (compressor.top_retain(def) && !(def.id in in_use_ids)) { in_use_ids[def.id] = true; in_use.push(def); } }); } var initializations = new Dictionary(); // pass 1: find out which symbols are directly used in // this scope (not in nested scopes). var scope = this; var tw = new TreeWalker(function(node, descend){ if (node !== self) { if (node instanceof AST_Defun) { if (!drop_funcs && scope === self) { var node_def = node.name.definition(); if (!(node_def.id in in_use_ids)) { in_use_ids[node_def.id] = true; in_use.push(node_def); } } initializations.add(node.name.name, node); return true; // don't go in nested scopes } if (node instanceof AST_Definitions && scope === self) { node.definitions.forEach(function(def){ if (!drop_vars) { var node_def = def.name.definition(); if (!(node_def.id in in_use_ids)) { in_use_ids[node_def.id] = true; in_use.push(node_def); } } if (def.value) { initializations.add(def.name.name, def.value); if (def.value.has_side_effects(compressor)) { def.value.walk(tw); } } }); return true; } if (assign_as_unused && node instanceof AST_Assign && node.operator == "=" && node.left instanceof AST_SymbolRef && scope === self) { node.right.walk(tw); return true; } if (node instanceof AST_SymbolRef) { var node_def = node.definition(); if (!(node_def.id in in_use_ids)) { in_use_ids[node_def.id] = true; in_use.push(node_def); } return true; } if (node instanceof AST_Scope) { var save_scope = scope; scope = node; descend(); scope = save_scope; return true; } } }); self.walk(tw); // pass 2: for every used symbol we need to walk its // initialization code to figure out if it uses other // symbols (that may not be in_use). for (var i = 0; i < in_use.length; ++i) { in_use[i].orig.forEach(function(decl){ // undeclared globals will be instanceof AST_SymbolRef var init = initializations.get(decl.name); if (init) init.forEach(functio ...
...
descend(node, this);
// Existing code relies on how AST_Node.optimize() worked, and omitting the
// following replacement call would result in degraded efficiency of both
// output and performance.
descend(node, this);
var opt = node.optimize(this);
if (was_scope && opt instanceof AST_Scope) {
opt.drop_unused(this);
descend(opt, this);
}
if (opt === node) opt._squeezed = true;
return opt;
}
});
...
find_variable = function (name){ if (name instanceof AST_Symbol) name = name.name; return this.variables.get(name) || (this.parent_scope && this.parent_scope.find_variable(name)); }
...
while (scope = compressor.parent(i++)) {
if (scope instanceof AST_Scope) break;
if (scope instanceof AST_Catch) {
scope = scope.argname.definition().scope;
break;
}
}
return scope.find_variable(name);
}
function make_node(ctor, orig, props) {
if (!props) props = {};
if (orig) {
if (!props.start) props.start = orig.start;
if (!props.end) props.end = orig.end;
...
hoist_declarations = function (compressor){ var self = this; if (compressor.has_directive("use asm")) return self; var hoist_funs = compressor.option("hoist_funs"); var hoist_vars = compressor.option("hoist_vars"); if (hoist_funs || hoist_vars) { var dirs = []; var hoisted = []; var vars = new Dictionary(), vars_found = 0, var_decl = 0; // let's count var_decl first, we seem to waste a lot of // space if we hoist `var` when there's only one. self.walk(new TreeWalker(function(node){ if (node instanceof AST_Scope && node !== self) return true; if (node instanceof AST_Var) { ++var_decl; return true; } })); hoist_vars = hoist_vars && var_decl > 1; var tt = new TreeTransformer( function before(node) { if (node !== self) { if (node instanceof AST_Directive) { dirs.push(node); return make_node(AST_EmptyStatement, node); } if (node instanceof AST_Defun && hoist_funs) { hoisted.push(node); return make_node(AST_EmptyStatement, node); } if (node instanceof AST_Var && hoist_vars) { node.definitions.forEach(function(def){ vars.set(def.name.name, def); ++vars_found; }); var seq = node.to_assignments(compressor); var p = tt.parent(); if (p instanceof AST_ForIn && p.init === node) { if (seq == null) { var def = node.definitions[0].name; return make_node(AST_SymbolRef, def, def); } return seq; } if (p instanceof AST_For && p.init === node) { return seq; } if (!seq) return make_node(AST_EmptyStatement, node); return make_node(AST_SimpleStatement, node, { body: seq }); } if (node instanceof AST_Scope) return node; // to avoid descending in nested scopes } } ); self = self.transform(tt); if (vars_found > 0) { // collect only vars which don't show up in self's arguments list var defs = []; vars.each(function(def, name){ if (self instanceof AST_Lambda && find_if(function(x){ return x.name == def.name.name }, self.argnames)) { vars.del(name); } else { def = def.clone(); def.value = null; defs.push(def); vars.set(name, def); } }); if (defs.length > 0) { // try to merge in assignments for (var i = 0; i < self.body.length;) { if (self.body[i] instanceof AST_SimpleStatement) { var expr = self.body[i].body, sym, assign; if (expr instanceof AST_Assign && expr.operator == "=" && (sym = expr.left) instanceof AST_Symbol && vars.has(sym.name)) { var def = vars.get(sym.name); if (def.value) break; def.value = expr.right; remove(defs, def); defs.push(def); self.body.splice(i, 1); continue; ...
...
clear_warnings: function() {
this.warnings_produced = {};
},
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
...
init_scope_vars = function (parent_scope){ this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` this.parent_scope = parent_scope; // the parent scope this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes this.cname = -1; // the current index for mangling functions/variables }
...
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Catch) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(save_scope);
descend();
scope = save_scope;
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
...
next_mangled = function (options){ var ext = this.enclosed; out: while (true) { var m = base54(++this.cname); if (!is_identifier(m)) continue; // skip over "do" // https://github.com/mishoo/UglifyJS2/issues/242 -- do not // shadow a name excepted from mangling. if (options.except.indexOf(m) >= 0) continue; // we must ensure that the mangled name does not shadow a name // from some parent scope that is referenced in this or in // inner scopes. for (var i = ext.length; --i >= 0;) { var sym = ext[i]; var name = sym.mangled_name || (sym.unmangleable(options) && sym.name); if (m == name) continue out; } return m; } }
...
var sym = this.orig[0];
if (!options.screw_ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
this.mangled_name = def.mangled_name || def.name;
} else
this.mangled_name = s.next_mangled(options, this);
if (this.global && cache) {
cache.set(this.name, this.mangled_name);
}
}
}
};
...
function AST_Seq(props){ if (props) { this.end = props.end;this.start = props.start;this.cdr = props.cdr;this.car = props.car;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
cons = function (x, y) { var seq = new AST_Seq(x); seq.car = x; seq.cdr = y; return seq; }
...
return seq;
},
$from_array: function(array) {
if (array.length == 0) return null;
if (array.length == 1) return array[0].clone();
var list = null;
for (var i = array.length; --i >= 0;) {
list = AST_Seq.cons(array[i], list);
}
var p = list;
while (p) {
if (p.cdr && !p.cdr.cdr) {
p.cdr = p.cdr.car;
break;
}
...
from_array = function (array) { if (array.length == 0) return null; if (array.length == 1) return array[0].clone(); var list = null; for (var i = array.length; --i >= 0;) { list = AST_Seq.cons(array[i], list); } var p = list; while (p) { if (p.cdr && !p.cdr.cdr) { p.cdr = p.cdr.car; break; } p = p.cdr; } return list; }
...
return statements;
};
function sequencesize(statements, compressor) {
if (statements.length < 2) return statements;
var seq = [], ret = [];
function push_seq() {
seq = AST_Seq.from_array(seq);
if (seq) ret.push(make_node(AST_SimpleStatement, seq, {
body: seq
}));
seq = [];
};
statements.forEach(function(stat){
if (stat instanceof AST_SimpleStatement) {
...
function AST_Seq(props){ if (props) { this.end = props.end;this.start = props.start;this.cdr = props.cdr;this.car = props.car;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self._do_print(output); // var p = output.parent(); // if (p instanceof AST_Statement) { // output.with_indent(output.next_indent(), function(){ // self._do_print(output); // }); // } else { // self._do_print(output); // } }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_do_print = function (output){ this.car.print(output); if (this.cdr) { output.comma(); if (output.should_break()) { output.newline(); output.indent(); } this.cdr.print(output); } }
...
arg.print(output);
});
});
output.space();
print_bracketed(self.body, output, true);
});
DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output);
});
/* -----[ exits ]----- */
AST_Exit.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
if (this.value) {
output.space();
...
_eq_null = function (pure_getters) { return this.cdr._eq_null(pure_getters); }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.car._walk(visitor); if (this.cdr) this.cdr._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
add = function (node) { var p = this; while (p) { if (!(p.cdr instanceof AST_Seq)) { var cell = AST_Seq.cons(p.cdr, node); return p.cdr = cell; } p = p.cdr; } }
...
};
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
ret.pop();
var left = prev.body;
if (left instanceof AST_Seq) {
left.add(right);
} else {
left = AST_Seq.cons(left, right);
}
return left.transform(compressor);
};
var ret = [], prev = null;
statements.forEach(function(stat){
...
drop_side_effect_free = function (compressor){ var cdr = this.cdr.drop_side_effect_free(compressor); if (cdr === this.cdr) return this; if (!cdr) return this.car; return make_node(AST_Seq, this, { car: this.car, cdr: cdr }); }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
has_side_effects = function (compressor){ return this.car.has_side_effects(compressor) || this.cdr.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
is_boolean = function (){ return this.cdr.is_boolean(); }
...
def(AST_Node, return_false);
def(AST_UnaryPrefix, function(){
return member(this.operator, unary_bool);
});
def(AST_Binary, function(){
return member(this.operator, binary_bool) ||
( (this.operator == "&&" || this.operator == "||") &&
this.left.is_boolean() && this.right.is_boolean() );
});
def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_boolean();
});
...
is_number = function (compressor){ return this.cdr.is_number(compressor); }
...
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function(){
return unary(this.operator);
});
var binary = makePredicate("- * / % & | ^ << >> >>>");
def(AST_Binary, function(compressor){
return binary(this.operator) || this.operator == "+"
&& this.left.is_number(compressor)
&& this.right.is_number(compressor);
});
def(AST_Assign, function(compressor){
return binary(this.operator.slice(0, -1))
|| this.operator == "=" && this.right.is_number(compressor);
});
def(AST_Seq, function(compressor){
...
is_string = function (compressor){ return this.cdr.is_string(compressor); }
...
def(AST_Node, return_false);
def(AST_String, return_true);
def(AST_UnaryPrefix, function(){
return this.operator == "typeof";
});
def(AST_Binary, function(compressor){
return this.operator == "+" &&
(this.left.is_string(compressor) || this.right.is_string(compressor));
});
def(AST_Assign, function(compressor){
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
def(AST_Seq, function(compressor){
return this.cdr.is_string(compressor);
});
...
len = function () { if (this.cdr instanceof AST_Seq) { return this.cdr.len() + 1; } else { return 2; } }
...
return p.cdr = cell;
}
p = p.cdr;
}
},
len: function() {
if (this.cdr instanceof AST_Seq) {
return this.cdr.len() + 1;
} else {
return 2;
}
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.car._walk(visitor);
...
needs_parens = function (output){
var p = output.parent();
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|| p instanceof AST_Unary // !(foo, bar, baz)
|| p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8
|| p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
|| p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
* ==> 20 (side effect, set a := 10 and b := 20) */
;
}
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
negate = function (compressor, first_in_statement){ return func.call(this, compressor, first_in_statement); }
...
continue loop;
}
//---
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
if (!stat.body.value && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array(stat.alternative).concat(ret);
var funs = extract_functions_from_statement_array(body);
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = null;
ret = funs.concat([ stat.transform(compressor) ]);
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_array = function () { var p = this, a = []; while (p) { a.push(p.car); if (p.cdr && !(p.cdr instanceof AST_Seq)) { a.push(p.cdr); break; } p = p.cdr; } return a; }
...
return self;
});
AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
if (compressor.option("sequences")) {
if (this.expression instanceof AST_Seq) {
var seq = this.expression;
var x = seq.to_array();
var e = this.clone();
e.expression = x.pop();
x.push(e);
seq = AST_Seq.from_array(x).transform(compressor);
return seq;
}
}
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_SimpleStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SimpleStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self.body.print(output); output.semicolon(); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.body._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
has_side_effects = function (compressor){ return this.body.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Debugger(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Directive(props){ if (props) { this.end = props.end;this.start = props.start;this.quote = props.quote;this.scope = props.scope;this.value = props.value;}}
n/a
function AST_SimpleStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_EmptyStatement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_StatementWithBody(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Jump(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Definitions(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions ;}}
n/a
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self.body.print(output); output.semicolon(); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_eval = function (){ throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start)); }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
function return_null() { return null; }
...
});
})(function(node, func){
node.DEFMETHOD("has_side_effects", func);
});
// tell me if a statement aborts
function aborts(thing) {
return thing && thing.aborts();
};
(function(def){
def(AST_Statement, return_null);
def(AST_Jump, return_this);
function block_aborts(){
var n = this.body.length;
return n > 0 && aborts(this.body[n - 1]);
...
negate = function (compressor, first_in_statement){ return func.call(this, compressor, first_in_statement); }
...
continue loop;
}
//---
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
if (!stat.body.value && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array(stat.alternative).concat(ret);
var funs = extract_functions_from_statement_array(body);
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = null;
ret = funs.concat([ stat.transform(compressor) ]);
...
function AST_StatementWithBody(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Statement(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_LabeledStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.label = props.label;}}
n/a
function AST_IterationStatement(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_With(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props .expression;}}
n/a
function AST_If(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.alternative = props .alternative;this.condition = props.condition;}}
n/a
function AST_StatementWithBody(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_do_print_body = function (output){ force_statement(this.body, output); }
...
DEFPRINT(AST_While, function(self, output){
output.print("while");
output.space();
output.with_parens(function(){
self.condition.print(output);
});
output.space();
self._do_print_body(output);
});
DEFPRINT(AST_For, function(self, output){
output.print("for");
output.space();
output.with_parens(function(){
if (self.init) {
if (self.init instanceof AST_Definitions) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.body._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
function AST_String(props){ if (props) { this.end = props.end;this.start = props.start;this.quote = props.quote;this.value = props .value;}}
n/a
function AST_Constant(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_String(props){ if (props) { this.end = props.end;this.start = props.start;this.quote = props.quote;this.value = props .value;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print_string(self.getValue(), self.quote, in_directive); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function return_true() { return true; }
...
def(AST_Node, return_false);
def(AST_String, return_true);
def(AST_UnaryPrefix, function(){
return this.operator == "typeof";
});
def(AST_Binary, function(compressor){
return this.operator == "+" &&
(this.left.is_string(compressor) || this.right.is_string(compressor));
});
def(AST_Assign, function(compressor){
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
def(AST_Seq, function(compressor){
return this.cdr.is_string(compressor);
});
...
function AST_Sub(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
function AST_PropAccess(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Sub(props){ if (props) { this.end = props.end;this.start = props.start;this.property = props.property;this.expression = props.expression;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self.expression.print(output); output.print("["); self.property.print(output); output.print("]"); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); this.property._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
drop_side_effect_free = function (compressor, first_in_statement){ if (this.expression.may_eq_null(compressor)) return this; var expression = this.expression.drop_side_effect_free(compressor, first_in_statement); if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement); var property = this.property.drop_side_effect_free(compressor); if (!property) return expression; return make_node(AST_Seq, this, { car: expression, cdr: property }); }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
has_side_effects = function (compressor){ return this.expression.may_eq_null(compressor) || this.expression.has_side_effects(compressor) || this.property.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Switch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props.expression;}}
n/a
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Switch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props.expression;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("switch"); output.space(); output.with_parens(function(){ self.expression.print(output); }); output.space(); var last = self.body.length - 1; if (last < 0) output.print("{}"); else output.with_block(function(){ self.body.forEach(function(branch, i){ output.indent(true); branch.print(output); if (i < last && branch.body.length > 0) output.newline(); }); }); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); walk_body(this, visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
has_side_effects = function (compressor){ return this.expression.has_side_effects(compressor) || any(this.body, compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_SwitchBranch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Default(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
function AST_Case(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props .expression;}}
n/a
function AST_SwitchBranch(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_do_print_body = function (output){ output.newline(); this.body.forEach(function(stmt){ output.indent(); stmt.print(output); output.newline(); }); }
...
DEFPRINT(AST_While, function(self, output){
output.print("while");
output.space();
output.with_parens(function(){
self.condition.print(output);
});
output.space();
self._do_print_body(output);
});
DEFPRINT(AST_For, function(self, output){
output.print("for");
output.space();
output.with_parens(function(){
if (self.init) {
if (self.init instanceof AST_Definitions) {
...
function block_aborts(){ var n = this.body.length; return n > 0 && aborts(this.body[n - 1]); }
...
});
})(function(node, func){
node.DEFMETHOD("has_side_effects", func);
});
// tell me if a statement aborts
function aborts(thing) {
return thing && thing.aborts();
};
(function(def){
def(AST_Statement, return_null);
def(AST_Jump, return_this);
function block_aborts(){
var n = this.body.length;
return n > 0 && aborts(this.body[n - 1]);
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Symbol(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SymbolAccessor(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
n/a
function AST_SymbolDeclaration(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this .name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_Label(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;this.references = props.references;this.initialize();}}
n/a
function AST_SymbolRef(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
n/a
function AST_LabelRef(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
n/a
function AST_This(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
function AST_Symbol(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ var def = self.definition(); output.print_name(def ? def.mangled_name || def.name : self.name); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
definition = function (){ return this.thedef; }
...
},
wrap_commonjs: function(name, export_all) {
var self = this;
var to_export = [];
if (export_all) {
self.figure_out_scope();
self.walk(new TreeWalker(function(node){
if (node instanceof AST_SymbolDeclaration && node.definition().
global) {
if (!find_if(function(n){ return n.name == node.name }, to_export))
to_export.push(node);
}
}));
}
var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "
;'] = exports; }({}, (function(){return this}())))";
wrapped_tl = parse(wrapped_tl);
...
global = function (){ return this.definition().global; }
...
return make_node_from_constant(value, orig);
}
def(AST_Node, noop);
def(AST_Dot, function(compressor, suffix){
return this.expression._find_defs(compressor, "." + this.property + suffix);
});
def(AST_SymbolRef, function(compressor, suffix){
if (!this.global()) return;
var name;
var defines = compressor.option("global_defs");
if (defines && HOP(defines, (name = this.name + suffix))) {
var node = to_node(defines[name], this);
var top = compressor.find_parent(AST_Toplevel);
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) {
...
mark_enclosed = function (options) { var def = this.definition(); var s = this.scope; while (s) { push_uniq(s.enclosed, def); if (options.keep_fnames) { s.functions.each(function(d) { push_uniq(def.scope.enclosed, d); }); } if (s === def.scope) break; s = s.parent_scope; } }
...
// later.
(node.scope = defun.parent_scope).def_function(node);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) {
defun.def_variable(node);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
...
reference = function (options) { this.definition().references.push(this); this.mark_enclosed(options); }
...
|| node instanceof AST_SymbolConst) {
defun.def_variable(node);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
undeclared = function (){ return this.definition().undeclared; }
...
|| this.alternative.has_side_effects(compressor);
});
def(AST_Unary, function(compressor){
return unary_side_effects(this.operator)
|| this.expression.has_side_effects(compressor);
});
def(AST_SymbolRef, function(compressor){
return this.undeclared();
});
def(AST_Object, function(compressor){
return any(this.properties, compressor);
});
def(AST_ObjectProperty, function(compressor){
return this.value.has_side_effects(compressor);
});
...
unmangleable = function (options){ return this.definition().unmangleable(options); }
...
|| this.orig[0] instanceof AST_SymbolDefun));
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
if (this.global && cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
}
else if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope;
var sym = this.orig[0];
if (!options.screw_ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
this.mangled_name = def.mangled_name || def.name;
...
unreferenced = function (){ return this.definition().references.length == 0 && !(this.scope.uses_eval || this.scope.uses_with); }
...
var trim = !compressor.option("keep_fargs");
for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i];
if (!(sym.definition().id in in_use_ids)) {
sym.__unused = true;
if (trim) {
a.pop();
compressor[sym.unreferenced() ? "warn" : "info"]("
;Dropping unused function argument {name} [{file}:{line},{col}]", {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
});
}
}
...
function AST_SymbolAccessor(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
n/a
function AST_Symbol(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SymbolAccessor(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
unmangleable = function (){ return true; }
...
|| this.orig[0] instanceof AST_SymbolDefun));
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
if (this.global && cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
}
else if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope;
var sym = this.orig[0];
if (!options.screw_ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
this.mangled_name = def.mangled_name || def.name;
...
function AST_SymbolCatch(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolDeclaration(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this .name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SymbolCatch(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function AST_SymbolConst(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolDeclaration(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this .name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SymbolConst(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function AST_SymbolDeclaration(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this .name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_Symbol(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SymbolVar(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolConst(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolDefun(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolLambda(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolCatch(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolDeclaration(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this .name = props.name;this.scope = props.scope;this.init = props.init;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function AST_SymbolDefun(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolDeclaration(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this .name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SymbolDefun(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function AST_SymbolFunarg(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolVar(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SymbolFunarg(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function AST_SymbolLambda(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolDeclaration(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this .name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SymbolLambda(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function AST_SymbolRef(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
n/a
function AST_Symbol(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SymbolRef(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_eq_null = function (pure_getters) { if (this.is_undefined) return true; if (!is_strict(pure_getters)) return false; var fixed = this.fixed_value(); return !fixed || fixed._eq_null(pure_getters); }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
_eval = function (compressor){ if (this._evaluating) throw def; this._evaluating = true; try { var fixed = this.fixed_value(); if (compressor.option("reduce_vars") && fixed) { if (compressor.option("unsafe")) { if (!HOP(fixed, "_evaluated")) { fixed._evaluated = ev(fixed, compressor); } return fixed._evaluated; } return ev(fixed, compressor); } } finally { this._evaluating = false; } throw def; }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
_find_defs = function (compressor, suffix){ if (!this.global()) return; var name; var defines = compressor.option("global_defs"); if (defines && HOP(defines, (name = this.name + suffix))) { var node = to_node(defines[name], this); var top = compressor.find_parent(AST_Toplevel); node.walk(new TreeWalker(function(node) { if (node instanceof AST_SymbolRef) { node.scope = top; node.thedef = top.def_global(node); } })); return node; } }
...
if (parent instanceof AST_Unary && unary_side_effects(parent.operator)) return parent.expression;
if (parent instanceof AST_Assign && parent.left === node) return node;
}
(function (def){
AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
if (!compressor.option("global_defs")) return;
var def = this._find_defs(compressor, "");
if (def) {
var node, parent = this, level = 0;
do {
node = parent;
parent = compressor.parent(level++);
} while (parent instanceof AST_PropAccess && parent.expression === node);
if (is_lhs(node, parent)) {
...
drop_side_effect_free = function () { return this.undeclared() ? this : null; }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
fixed_value = function () { var fixed = this.definition().fixed; if (!fixed || fixed instanceof AST_Node) return fixed; return fixed(); }
...
if (reduce_vars) {
if (node instanceof AST_Toplevel) node.globals.each(reset_def);
if (node instanceof AST_Scope) node.variables.each(reset_def);
if (node instanceof AST_SymbolRef) {
var d = node.definition();
d.references.push(node);
if (d.fixed === undefined || !is_safe(d)
|| is_modified(node, 0, node.fixed_value() instanceof AST_Lambda)) {
d.fixed = false;
}
}
if (node instanceof AST_SymbolCatch) {
node.definition().fixed = false;
}
if (node instanceof AST_VarDef) {
...
has_side_effects = function (compressor){ return this.undeclared(); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
function AST_SymbolVar(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolDeclaration(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this .name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_SymbolFunarg(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
n/a
function AST_SymbolVar(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props.name;this.scope = props.scope;this.init = props.init;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function AST_This(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
function AST_Symbol(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_This(props){ if (props) { this.end = props.end;this.start = props.start;this.thedef = props.thedef;this.name = props .name;this.scope = props.scope;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("this"); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function return_null() { return null; }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
function return_false() { return false; }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Throw(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
function AST_Exit(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Throw(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self._do_print(output, "throw"); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_Token(props){ if (props) { this.raw = props.raw;this.file = props.file;this.comments_before = props.comments_before ;this.nlb = props.nlb;this.endpos = props.endpos;this.endcol = props.endcol;this.endline = props.endline;this.pos = props.pos;this .col = props.col;this.line = props.line;this.value = props.value;this.type = props.type;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Token(props){ if (props) { this.raw = props.raw;this.file = props.file;this.comments_before = props.comments_before ;this.nlb = props.nlb;this.endpos = props.endpos;this.endcol = props.endcol;this.endline = props.endline;this.pos = props.pos;this .col = props.col;this.line = props.line;this.value = props.value;this.type = props.type;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function AST_Toplevel(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.globals = props .globals;}}
n/a
function AST_Scope(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props. cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Toplevel(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.cname = props .cname;this.enclosed = props.enclosed;this.parent_scope = props.parent_scope;this.uses_eval = props.uses_eval;this.uses_with = props .uses_with;this.functions = props.functions;this.variables = props.variables;this.directives = props.directives;this.globals = props .globals;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ display_body(self.body, true, output, true); output.print(""); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_default_mangler_options = function (options){ return defaults(options, { eval : false, except : [], keep_fnames : false, screw_ie8 : true, sort : false, // Ignored. Flag retained for backwards compatibility. toplevel : false, }); }
...
screw_ie8 : true,
sort : false, // Ignored. Flag retained for backwards compatibility.
toplevel : false,
});
});
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options);
// Never mangle arguments
options.except.push('arguments');
// We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's
// present (and for AST_SymbolRef-s it'll use the mangled name of
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
compute_char_frequency = function (options){ options = this._default_mangler_options(options); var tw = new TreeWalker(function(node){ if (node instanceof AST_Constant) base54.consider(node.print_to_string()); else if (node instanceof AST_Return) base54.consider("return"); else if (node instanceof AST_Throw) base54.consider("throw"); else if (node instanceof AST_Continue) base54.consider("continue"); else if (node instanceof AST_Break) base54.consider("break"); else if (node instanceof AST_Debugger) base54.consider("debugger"); else if (node instanceof AST_Directive) base54.consider(node.value); else if (node instanceof AST_While) base54.consider("while"); else if (node instanceof AST_Do) base54.consider("do while"); else if (node instanceof AST_If) { base54.consider("if"); if (node.alternative) base54.consider("else"); } else if (node instanceof AST_Var) base54.consider("var"); else if (node instanceof AST_Const) base54.consider("const"); else if (node instanceof AST_Lambda) base54.consider("function"); else if (node instanceof AST_For) base54.consider("for"); else if (node instanceof AST_ForIn) base54.consider("for in"); else if (node instanceof AST_Switch) base54.consider("switch"); else if (node instanceof AST_Case) base54.consider("case"); else if (node instanceof AST_Default) base54.consider("default"); else if (node instanceof AST_With) base54.consider("with"); else if (node instanceof AST_ObjectSetter) base54.consider("set" + node.key); else if (node instanceof AST_ObjectGetter) base54.consider("get" + node.key); else if (node instanceof AST_ObjectKeyVal) base54.consider(node.key); else if (node instanceof AST_New) base54.consider("new"); else if (node instanceof AST_This) base54.consider("this"); else if (node instanceof AST_Try) base54.consider("try"); else if (node instanceof AST_Catch) base54.consider("catch"); else if (node instanceof AST_Finally) base54.consider("finally"); else if (node instanceof AST_Symbol && node.unmangleable(options)) base54.consider(node.name); else if (node instanceof AST_Unary || node instanceof AST_Binary) base54.consider(node.operator); else if (node instanceof AST_Dot) base54.consider(node.property); }); this.walk(tw); base54.sort(); }
...
// Compression
uAST.figure_out_scope();
uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional)
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
...
def_global = function (node){ var globals = this.globals, name = node.name; if (globals.has(name)) { return globals.get(name); } else { var g = new SymbolDef(this, globals.size(), node); g.undeclared = true; g.global = true; globals.set(name, g); return g; } }
...
var defines = compressor.option("global_defs");
if (defines && HOP(defines, (name = this.name + suffix))) {
var node = to_node(defines[name], this);
var top = compressor.find_parent(AST_Toplevel);
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) {
node.scope = top;
node.thedef = top.def_global(node);
}
}));
return node;
}
});
})(function(node, func){
node.DEFMETHOD("_find_defs", func);
...
figure_out_scope = function (options){ options = defaults(options, { cache: null, screw_ie8: true, }); // pass 1: setup scope chaining and handle definitions var self = this; var scope = self.parent_scope = null; var labels = new Dictionary(); var defun = null; var tw = new TreeWalker(function(node, descend){ if (node instanceof AST_Catch) { var save_scope = scope; scope = new AST_Scope(node); scope.init_scope_vars(save_scope); descend(); scope = save_scope; return true; } if (node instanceof AST_Scope) { node.init_scope_vars(scope); var save_scope = scope; var save_defun = defun; var save_labels = labels; defun = scope = node; labels = new Dictionary(); descend(); scope = save_scope; defun = save_defun; labels = save_labels; return true; // don't descend again in TreeWalker } if (node instanceof AST_LabeledStatement) { var l = node.label; if (labels.has(l.name)) { throw new Error(string_template("Label {name} defined twice", l)); } labels.set(l.name, l); descend(); labels.del(l.name); return true; // no descend again } if (node instanceof AST_With) { for (var s = scope; s; s = s.parent_scope) s.uses_with = true; return; } if (node instanceof AST_Symbol) { node.scope = scope; } if (node instanceof AST_Label) { node.thedef = node; node.references = []; } if (node instanceof AST_SymbolLambda) { defun.def_function(node); } else if (node instanceof AST_SymbolDefun) { // Careful here, the scope where this should be defined is // the parent scope. The reason is that we enter a new // scope when we encounter the AST_Defun node (which is // instanceof AST_Scope) but we get to the symbol a bit // later. (node.scope = defun.parent_scope).def_function(node); } else if (node instanceof AST_SymbolVar || node instanceof AST_SymbolConst) { defun.def_variable(node); if (defun !== scope) { node.mark_enclosed(options); var def = scope.find_variable(node); if (node.thedef !== def) { node.thedef = def; node.reference(options); } } } else if (node instanceof AST_SymbolCatch) { scope.def_variable(node).defun = defun; } else if (node instanceof AST_LabelRef) { var sym = labels.get(node.name); if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", { name: node.name, line: node.start.line, col: node.start.col })); node.thedef = sym; } }); self.walk(tw); // pass 2: find back references and eval var func = null; var globals = self.globals = new Dictionary(); var tw = new TreeWalker(function(node, descend){ if (node instanceof AST_Lambda) { var prev_func = func; func = node; descend(); func = prev_func; return true; } if (node instanceof AST_LoopControl && node.label) { node.label.thedef.references.push(node); return true; } if (node instanceof AST_SymbolRef) { var name = node.name; if (name == "eval" && tw.parent() instanceof AST_Call) { for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { s.uses_eval = true; } } var sym = node.scope.fi ...
...
```javascript
function uglify(ast, options, mangle) {
// Conversion from SpiderMonkey AST to internal format
var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
// Compression
uAST.figure_out_scope();
uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional)
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
...
mangle_names = function (options){ options = this._default_mangler_options(options); // Never mangle arguments options.except.push('arguments'); // We only need to mangle declaration nodes. Special logic wired // into the code generator will display the mangled name if it's // present (and for AST_SymbolRef-s it'll use the mangled name of // the AST_SymbolDeclaration that it points to). var lname = -1; var to_mangle = []; if (options.cache) { this.globals.each(function(symbol){ if (options.except.indexOf(symbol.name) < 0) { to_mangle.push(symbol); } }); } var tw = new TreeWalker(function(node, descend){ if (node instanceof AST_LabeledStatement) { // lname is incremented when we get to the AST_Label var save_nesting = lname; descend(); lname = save_nesting; return true; // don't descend again in TreeWalker } if (node instanceof AST_Scope) { var p = tw.parent(), a = []; node.variables.each(function(symbol){ if (options.except.indexOf(symbol.name) < 0) { a.push(symbol); } }); to_mangle.push.apply(to_mangle, a); return; } if (node instanceof AST_Label) { var name; do name = base54(++lname); while (!is_identifier(name)); node.mangled_name = name; return true; } if (options.screw_ie8 && node instanceof AST_SymbolCatch) { to_mangle.push(node.definition()); return; } }); this.walk(tw); to_mangle.forEach(function(def){ def.mangle(options) }); if (options.cache) { options.cache.cname = this.cname; } }
...
uAST.figure_out_scope();
uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional)
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
...
scope_warnings = function (options){ options = defaults(options, { assign_to_global : true, eval : true, func_arguments : true, nested_defuns : true, undeclared : false, // this makes a lot of noise unreferenced : true, }); var tw = new TreeWalker(function(node){ if (options.undeclared && node instanceof AST_SymbolRef && node.undeclared()) { // XXX: this also warns about JS standard names, // i.e. Object, Array, parseInt etc. Should add a list of // exceptions. AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", { name: node.name, file: node.start.file, line: node.start.line, col: node.start.col }); } if (options.assign_to_global) { var sym = null; if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) sym = node.left; else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef) sym = node.init; if (sym && (sym.undeclared() || (sym.global() && sym.scope !== sym.definition().scope))) { AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", { msg: sym.undeclared() ? "Accidental global?" : "Assignment to global", name: sym.name, file: sym.start.file, line: sym.start.line, col: sym.start.col }); } } if (options.eval && node instanceof AST_SymbolRef && node.undeclared() && node.name == "eval") { AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start); } if (options.unreferenced && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label) && !(node instanceof AST_SymbolCatch) && node.unreferenced()) { AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", { type: node instanceof AST_Label ? "Label" : "Symbol", name: node.name, file: node.start.file, line: node.start.line, col: node.start.col }); } if (options.func_arguments && node instanceof AST_Lambda && node.uses_arguments) { AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", { name: node.name ? node.name.name : "anonymous", file: node.start.file, line: node.start.line, col: node.start.col }); } if (options.nested_defuns && node instanceof AST_Defun && !(tw.parent() instanceof AST_Scope)) { AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", { name: node.name.name, type: tw.parent().TYPE, file: node.start.file, line: node.start.line, col: node.start.col }); } }); this.walk(tw); }
n/a
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
wrap_commonjs = function (name, export_all) { var self = this; var to_export = []; if (export_all) { self.figure_out_scope(); self.walk(new TreeWalker(function(node){ if (node instanceof AST_SymbolDeclaration && node.definition().global) { if (!find_if(function(n){ return n.name == node.name }, to_export)) to_export.push(node); } })); } var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "'] = exports; }({}, (function(){return this}())))"; wrapped_tl = parse(wrapped_tl); wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ if (node instanceof AST_Directive) { switch (node.value) { case "$ORIG": return MAP.splice(self.body); case "$EXPORTS": var body = []; to_export.forEach(function(sym){ body.push(new AST_SimpleStatement({ body: new AST_Assign({ left: new AST_Sub({ expression: new AST_SymbolRef({ name: "exports" }), property: new AST_String({ value: sym.name }), }), operator: "=", right: new AST_SymbolRef(sym), }), })); }); return MAP.splice(body); } } })); return wrapped_tl; }
n/a
wrap_enclose = function (arg_parameter_pairs) { var self = this; var args = []; var parameters = []; arg_parameter_pairs.forEach(function(pair) { var splitAt = pair.lastIndexOf(":"); args.push(pair.substr(0, splitAt)); parameters.push(pair.substr(splitAt + 1)); }); var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")"; wrapped_tl = parse(wrapped_tl); wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ if (node instanceof AST_Directive && node.value == "$ORIG") { return MAP.splice(self.body); } })); return wrapped_tl; }
n/a
function AST_True(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Boolean(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_True(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function return_true() { return true; }
...
def(AST_Node, return_false);
def(AST_UnaryPrefix, function(){
return member(this.operator, unary_bool);
});
def(AST_Binary, function(){
return member(this.operator, binary_bool) ||
( (this.operator == "&&" || this.operator == "||") &&
this.left.is_boolean() && this.right.is_boolean() );
});
def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_boolean();
});
...
function AST_Try(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.bfinally = props .bfinally;this.bcatch = props.bcatch;}}
n/a
function AST_Block(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Try(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.bfinally = props .bfinally;this.bcatch = props.bcatch;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("try"); output.space(); print_bracketed(self.body, output); if (self.bcatch) { output.space(); self.bcatch.print(output); } if (self.bfinally) { output.space(); self.bfinally.print(output); } }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ walk_body(this, visitor); if (this.bcatch) this.bcatch._walk(visitor); if (this.bfinally) this.bfinally._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
add_source_map = function (stream){ generator(this, stream); }
...
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope
) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
...
has_side_effects = function (compressor){ return any(this.body, compressor) || this.bcatch && this.bcatch.has_side_effects(compressor) || this.bfinally && this.bfinally.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_Unary(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression;this.operator = props.operator;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_UnaryPrefix(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression;this .operator = props.operator;}}
n/a
function AST_UnaryPostfix(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression; this.operator = props.operator;}}
n/a
function AST_Unary(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression;this.operator = props.operator;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
drop_side_effect_free = function (compressor, first_in_statement){ if (unary_side_effects(this.operator)) return this; if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) return null; var expression = this.expression.drop_side_effect_free(compressor, first_in_statement); if (first_in_statement && this instanceof AST_UnaryPrefix && is_iife_call(expression)) { if (expression === this.expression && this.operator.length === 1) return this; return make_node(AST_UnaryPrefix, this, { operator: this.operator.length === 1 ? this.operator : "!", expression: expression }); } return expression; }
...
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor
, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
...
has_side_effects = function (compressor){ return unary_side_effects(this.operator) || this.expression.has_side_effects(compressor); }
...
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= value_has_side_effects;
continue;
}
// Detect lvalues in var value.
...
is_number = function (){ return unary(this.operator); }
...
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function(){
return unary(this.operator);
});
var binary = makePredicate("- * / % & | ^ << >> >>>");
def(AST_Binary, function(compressor){
return binary(this.operator) || this.operator == "+"
&& this.left.is_number(compressor)
&& this.right.is_number(compressor);
});
def(AST_Assign, function(compressor){
return binary(this.operator.slice(0, -1))
|| this.operator == "=" && this.right.is_number(compressor);
});
def(AST_Seq, function(compressor){
...
lift_sequences = function (compressor){ if (compressor.option("sequences")) { if (this.expression instanceof AST_Seq) { var seq = this.expression; var x = seq.to_array(); var e = this.clone(); e.expression = x.pop(); x.push(e); seq = AST_Seq.from_array(x).transform(compressor); return seq; } } return this; }
...
return seq;
}
}
return this;
});
OPT(AST_UnaryPostfix, function(self, compressor){
return self.lift_sequences(compressor);
});
OPT(AST_UnaryPrefix, function(self, compressor){
var e = self.expression;
if (self.operator == "delete"
&& !(e instanceof AST_SymbolRef
|| e instanceof AST_PropAccess
...
needs_parens = function (output){ var p = output.parent(); return p instanceof AST_PropAccess && p.expression === this || p instanceof AST_Call && p.expression === this; }
...
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
} else {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_UnaryPostfix(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression; this.operator = props.operator;}}
n/a
function AST_Unary(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression;this.operator = props.operator;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_UnaryPostfix(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression; this.operator = props.operator;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self.expression.print(output); output.print(self.operator); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function return_false() { return false; }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
function AST_UnaryPrefix(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression;this .operator = props.operator;}}
n/a
function AST_Unary(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression;this.operator = props.operator;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_UnaryPrefix(props){ if (props) { this.end = props.end;this.start = props.start;this.expression = props.expression;this .operator = props.operator;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ var op = self.operator; output.print(op); if (/^[a-z]/i.test(op) || (/[+-]$/.test(op) && self.expression instanceof AST_UnaryPrefix && /^[+-]/.test(self.expression.operator))) { output.space(); } self.expression.print(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_eq_null = function () { return this.operator == "void"; }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
_eval = function (compressor){ var e = this.expression; switch (this.operator) { case "!": return !ev(e, compressor); case "typeof": // Function would be evaluated to an array and so typeof would // incorrectly return 'object'. Hence making is a special case. if (e instanceof AST_Function) return typeof function(){}; e = ev(e, compressor); // typeof <RegExp> returns "object" or "function" on different platforms // so cannot evaluate reliably if (e instanceof RegExp) throw def; return typeof e; case "void": return void ev(e, compressor); case "~": return ~ev(e, compressor); case "-": return -ev(e, compressor); case "+": return +ev(e, compressor); } throw def; }
...
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
});
var unaryPrefix = makePredicate("! ~ - + void");
...
is_boolean = function (){ return member(this.operator, unary_bool); }
...
def(AST_Node, return_false);
def(AST_UnaryPrefix, function(){
return member(this.operator, unary_bool);
});
def(AST_Binary, function(){
return member(this.operator, binary_bool) ||
( (this.operator == "&&" || this.operator == "||") &&
this.left.is_boolean() && this.right.is_boolean() );
});
def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_boolean();
});
...
is_string = function (){ return this.operator == "typeof"; }
...
def(AST_Node, return_false);
def(AST_String, return_true);
def(AST_UnaryPrefix, function(){
return this.operator == "typeof";
});
def(AST_Binary, function(compressor){
return this.operator == "+" &&
(this.left.is_string(compressor) || this.right.is_string(compressor));
});
def(AST_Assign, function(compressor){
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
def(AST_Seq, function(compressor){
return this.cdr.is_string(compressor);
});
...
negate = function (compressor, first_in_statement){ return func.call(this, compressor, first_in_statement); }
...
continue loop;
}
//---
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
if (!stat.body.value && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array(stat.alternative).concat(ret);
var funs = extract_functions_from_statement_array(body);
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = null;
ret = funs.concat([ stat.transform(compressor) ]);
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
function AST_Undefined(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
function AST_Atom(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Undefined(props){ if (props) { this.end = props.end;this.start = props.start;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
function return_true() { return true; }
...
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
function AST_Var(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions;}}
n/a
function AST_Definitions(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions ;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_Var(props){ if (props) { this.end = props.end;this.start = props.start;this.definitions = props.definitions;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self._do_print(output, "var"); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
function AST_VarDef(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.name = props .name;}}
n/a
function AST_Node(props){ if (props) { this.end = props.end;this.start = props.start;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_VarDef(props){ if (props) { this.end = props.end;this.start = props.start;this.value = props.value;this.name = props .name;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ self.name.print(output); if (self.value) { output.space(); output.print("="); output.space(); var p = output.parent(1); var noin = p instanceof AST_For || p instanceof AST_ForIn; parenthesize_for_noin(self.value, output, noin); } }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.name._walk(visitor); if (this.value) this.value._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
optimize = function (compressor){ var self = this; if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; return opt; }
...
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function AST_While(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
function AST_DWLoop(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_While(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.condition = props .condition;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("while"); output.space(); output.with_parens(function(){ self.condition.print(output); }); output.space(); self._do_print_body(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.condition._walk(visitor); this.body._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
function AST_With(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props .expression;}}
n/a
function AST_StatementWithBody(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;}}
n/a
DEFMETHOD = function (name, method) { this.prototype[name] = method; }
...
return opt;
}
});
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
...
function AST_With(props){ if (props) { this.end = props.end;this.start = props.start;this.body = props.body;this.expression = props .expression;}}
...
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
...
_codegen = function (self, output){ output.print("with"); output.space(); output.with_parens(function(){ self.expression.print(output); }); output.space(); self._do_print_body(output); }
...
expr.print(output);
});
});
});
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
...
_walk = function (visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); this.body._walk(visitor); }); }
...
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
...
to_mozilla_ast = function () { return set_moz_loc(this, handler(this)); }
...
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
...
transform = function (tw, in_list){ var x, y; tw.push(this); if (tw.before) x = tw.before(this, descend, in_list); if (x === undefined) { if (!tw.after) { x = this; descend(x, tw); } else { tw.stack[tw.stack.length - 1] = x = this; descend(x, tw); y = tw.after(x, in_list); if (y !== undefined) x = y; } } tw.pop(this); return x; }
...
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw"
;, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this);
},
...
function Compressor(options, false_by_default) { if (!(this instanceof Compressor)) return new Compressor(options, false_by_default); TreeTransformer.call(this, this.before, this.after); this.options = defaults(options, { angular : false, booleans : !false_by_default, cascade : !false_by_default, collapse_vars : !false_by_default, comparisons : !false_by_default, conditionals : !false_by_default, dead_code : !false_by_default, drop_console : false, drop_debugger : !false_by_default, evaluate : !false_by_default, expression : false, global_defs : {}, hoist_funs : !false_by_default, hoist_vars : false, if_return : !false_by_default, join_vars : !false_by_default, keep_fargs : true, keep_fnames : false, keep_infinity : false, loops : !false_by_default, negate_iife : !false_by_default, passes : 1, properties : !false_by_default, pure_getters : !false_by_default && "strict", pure_funcs : null, reduce_vars : !false_by_default, screw_ie8 : true, sequences : !false_by_default, side_effects : !false_by_default, switches : !false_by_default, top_retain : null, toplevel : !!(options && options["top_retain"]), unsafe : false, unsafe_comps : false, unsafe_math : false, unsafe_proto : false, unused : !false_by_default, warnings : true, }, true); var pure_funcs = this.options["pure_funcs"]; if (typeof pure_funcs == "function") { this.pure_funcs = pure_funcs; } else { this.pure_funcs = pure_funcs ? function(node) { return pure_funcs.indexOf(node.expression.print_to_string()) < 0; } : return_true; } var top_retain = this.options["top_retain"]; if (top_retain instanceof RegExp) { this.top_retain = function(def) { return top_retain.test(def.name); }; } else if (typeof top_retain == "function") { this.top_retain = top_retain; } else if (top_retain) { if (typeof top_retain == "string") { top_retain = top_retain.split(/,/); } this.top_retain = function(def) { return top_retain.indexOf(def.name) >= 0; }; } var sequences = this.options["sequences"]; this.sequences_limit = sequences == 1 ? 200 : sequences | 0; this.warnings_produced = {}; }
...
```javascript
function uglify(ast, options, mangle) {
// Conversion from SpiderMonkey AST to internal format
var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
// Compression
uAST.figure_out_scope();
uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional)
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
...
before = function (node, descend, in_list) { if (node._squeezed) return node; var was_scope = false; if (node instanceof AST_Scope) { node = node.hoist_declarations(this); was_scope = true; } // Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize() // would call AST_Node.transform() if a different instance of AST_Node is // produced after OPT(). // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction. // Migrate and defer all children's AST_Node.transform() to below, which // will now happen after this parent AST_Node has been properly substituted // thus gives a consistent AST snapshot. descend(node, this); // Existing code relies on how AST_Node.optimize() worked, and omitting the // following replacement call would result in degraded efficiency of both // output and performance. descend(node, this); var opt = node.optimize(this); if (was_scope && opt instanceof AST_Scope) { opt.drop_unused(this); descend(opt, this); } if (opt === node) opt._squeezed = true; return opt; }
...
(function(undefined){
function _(node, descend) {
node.DEFMETHOD("transform", function(tw, in_list){
var x, y;
tw.push(this);
if (tw.before) x = tw.before(this, descend, in_list);
if (x === undefined) {
if (!tw.after) {
x = this;
descend(x, tw);
} else {
tw.stack[tw.stack.length - 1] = x = this;
descend(x, tw);
...
clear_warnings = function () { this.warnings_produced = {}; }
n/a
compress = function (node) { if (this.option("expression")) { node = node.process_expression(true); } var passes = +this.options.passes || 1; for (var pass = 0; pass < passes && pass < 3; ++pass) { if (pass > 0 || this.option("reduce_vars")) node.reset_opt_flags(this, true); node = node.transform(this); } if (this.option("expression")) { node = node.process_expression(false); } return node; }
...
```javascript
function uglify(ast, options, mangle) {
// Conversion from SpiderMonkey AST to internal format
var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
// Compression
uAST.figure_out_scope();
uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional)
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
...
info = function () { if (this.options.warnings == "verbose") { AST_Node.warn.apply(AST_Node, arguments); } }
...
if (var_defs.length === 0) {
statements[prev_stat_index] = make_node(AST_EmptyStatement, self);
var_defs_removed = true;
}
// Further optimize statement after substitution.
stat.reset_opt_flags(compressor);
compressor.info("Collapsing " + (is_constant ? "constant" :
x22;variable") +
" " + var_name + " [{file}:{line},{col}]", node.start);
CHANGED = true;
return value;
}
}
function process_for_angular(statements) {
...
option = function (key) { return this.options[key] }
...
this.warnings_produced = {};
};
Compressor.prototype = new TreeTransformer;
merge(Compressor.prototype, {
option: function(key) { return this.options[key] },
compress: function(node) {
if (this.option("expression")) {
node = node.process_expression(true);
}
var passes = +this.options.passes || 1;
for (var pass = 0; pass < passes && pass < 3; ++pass) {
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this, true);
node = node.transform(this);
...
warn = function (text, props) { if (this.options.warnings) { // only emit unique warnings var message = string_template(text, props); if (!(message in this.warnings_produced)) { this.warnings_produced[message] = true; AST_Node.warn.apply(AST_Node, arguments); } } }
...
stat.definitions.forEach(function(def) {
if (def.value && def.value instanceof AST_Lambda) {
a.push(make_injector(def.value, def.name));
}
});
}
else {
compressor.warn("Unknown statement marked with @ngInject [{file
}:{line},{col}]", token);
}
}
}
}
return a;
}, []);
...
function DefaultsError(msg, defs) { this.message = msg; this.defs = defs; }
n/a
croak = function (msg, defs) { throw new DefaultsError(msg, defs); }
...
};
function defaults(args, defs, croak) {
if (args === true)
args = {};
var ret = args || {};
if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i))
DefaultsError.croak("`" + i + "` is not a supported option"
;, defs);
for (var i in defs) if (HOP(defs, i)) {
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
}
return ret;
};
function merge(obj, ext) {
...
function DefaultsError(msg, defs) { this.message = msg; this.defs = defs; }
n/a
function Dictionary() { this._values = Object.create(null); this._size = 0; }
n/a
fromObject = function (obj) { var dict = new Dictionary(); dict._size = merge(dict._values, obj); return dict; }
n/a
add = function (key, val) { if (this.has(key)) { this.get(key).push(val); } else { this.set(key, [ val ]); } return this; }
...
};
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
ret.pop();
var left = prev.body;
if (left instanceof AST_Seq) {
left.add(right);
} else {
left = AST_Seq.cons(left, right);
}
return left.transform(compressor);
};
var ret = [], prev = null;
statements.forEach(function(stat){
...
del = function (key) { if (this.has(key)) { --this._size; delete this._values["$" + key]; } return this; }
...
if (vars_found > 0) {
// collect only vars which don't show up in self's arguments list
var defs = [];
vars.each(function(def, name){
if (self instanceof AST_Lambda
&& find_if(function(x){ return x.name == def.name.name },
self.argnames)) {
vars.del(name);
} else {
def = def.clone();
def.value = null;
defs.push(def);
vars.set(name, def);
}
});
...
each = function (f) { for (var i in this._values) f(this._values[i], i.substr(1)); }
...
d.fixed = false;
}
});
var tw = new TreeWalker(function(node, descend){
node._squeezed = false;
node._optimized = false;
if (reduce_vars) {
if (node instanceof AST_Toplevel) node.globals.each(reset_def);
if (node instanceof AST_Scope) node.variables.each(reset_def);
if (node instanceof AST_SymbolRef) {
var d = node.definition();
d.references.push(node);
if (d.fixed === undefined || !is_safe(d)
|| is_modified(node, 0, node.fixed_value() instanceof AST_Lambda)) {
d.fixed = false;
...
get = function (key) { return this._values["$" + key] }
...
self.walk(tw);
// pass 2: for every used symbol we need to walk its
// initialization code to figure out if it uses other
// symbols (that may not be in_use).
for (var i = 0; i < in_use.length; ++i) {
in_use[i].orig.forEach(function(decl){
// undeclared globals will be instanceof AST_SymbolRef
var init = initializations.get(decl.name);
if (init) init.forEach(function(init){
var tw = new TreeWalker(function(node){
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
...
has = function (key) { return ("$" + key) in this._values }
...
// try to merge in assignments
for (var i = 0; i < self.body.length;) {
if (self.body[i] instanceof AST_SimpleStatement) {
var expr = self.body[i].body, sym, assign;
if (expr instanceof AST_Assign
&& expr.operator == "="
&& (sym = expr.left) instanceof AST_Symbol
&& vars.has(sym.name))
{
var def = vars.get(sym.name);
if (def.value) break;
def.value = expr.right;
remove(defs, def);
defs.push(def);
self.body.splice(i, 1);
...
map = function (f) { var ret = []; for (var i in this._values) ret.push(f(this._values[i], i.substr(1))); return ret; }
...
}
function process_for_angular(statements) {
function has_inject(comment) {
return /@ngInject/.test(comment.value);
}
function make_arguments_names_list(func) {
return func.argnames.map(function(sym){
return make_node(AST_String, sym, { value: sym.name });
});
}
function make_array(orig, elements) {
return make_node(AST_Array, orig, { elements: elements });
}
function make_injector(func, name) {
...
set = function (key, val) { if (!this.has(key)) ++this._size; this._values["$" + key] = val; return this; }
...
}
if (node instanceof AST_Defun && hoist_funs) {
hoisted.push(node);
return make_node(AST_EmptyStatement, node);
}
if (node instanceof AST_Var && hoist_vars) {
node.definitions.forEach(function(def){
vars.set(def.name.name, def);
++vars_found;
});
var seq = node.to_assignments(compressor);
var p = tt.parent();
if (p instanceof AST_ForIn && p.init === node) {
if (seq == null) {
var def = node.definitions[0].name;
...
size = function () { return this._size; }
...
});
AST_Toplevel.DEFMETHOD("def_global", function(node){
var globals = this.globals, name = node.name;
if (globals.has(name)) {
return globals.get(name);
} else {
var g = new SymbolDef(this, globals.size(), node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
return g;
}
});
...
toObject = function () { return this._values }
n/a
function JS_Parse_Error(message, filename, line, col, pos) { this.message = message; this.filename = filename; this.line = line; this.col = col; this.pos = pos; }
n/a
function JS_Parse_Error(message, filename, line, col, pos) { this.message = message; this.filename = filename; this.line = line; this.col = col; this.pos = pos; }
n/a
function MAP(a, f, backwards) { var ret = [], top = [], i; function doit() { var val = f(a[i], i); var is_last = val instanceof Last; if (is_last) val = val.v; if (val instanceof AtTop) { val = val.v; if (val instanceof Splice) { top.push.apply(top, backwards ? val.v.slice().reverse() : val.v); } else { top.push(val); } } else if (val !== skip) { if (val instanceof Splice) { ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v); } else { ret.push(val); } } return is_last; }; if (a instanceof Array) { if (backwards) { for (i = a.length; --i >= 0;) if (doit()) break; ret.reverse(); top.reverse(); } else { for (i = 0; i < a.length; ++i) if (doit()) break; } } else { for (i in a) if (HOP(a, i)) if (doit()) break; } return top.concat(ret); }
n/a
at_top = function (val) { return new AtTop(val) }
n/a
last = function (val) { return new Last(val) }
...
// self._do_print(output);
// }
});
DEFPRINT(AST_Dot, function(self, output){
var expr = self.expression;
expr.print(output);
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
}
}
output.print(".");
// the name after dot would be mapped about here.
output.add_mapping(self.end);
output.print_name(self.property);
...
splice = function (val) { return new Splice(val) }
...
parameters.push(pair.substr(splitAt + 1));
});
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join
(",") + ")";
wrapped_tl = parse(wrapped_tl);
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
if (node instanceof AST_Directive && node.value == "$ORIG") {
return MAP.splice(self.body);
}
}));
return wrapped_tl;
},
wrap_commonjs: function(name, export_all) {
var self = this;
var to_export = [];
...
function SymbolDef(scope, index, orig) { this.name = orig.name; this.orig = [ orig ]; this.scope = scope; this.references = []; this.global = false; this.mangled_name = null; this.undeclared = false; this.index = index; this.id = SymbolDef.next_id++; }
n/a
mangle = function (options) { var cache = options.cache && options.cache.props; if (this.global && cache && cache.has(this.name)) { this.mangled_name = cache.get(this.name); } else if (!this.mangled_name && !this.unmangleable(options)) { var s = this.scope; var sym = this.orig[0]; if (!options.screw_ie8 && sym instanceof AST_SymbolLambda) s = s.parent_scope; var def; if (this.defun && (def = this.defun.variables.get(this.name))) { this.mangled_name = def.mangled_name || def.name; } else this.mangled_name = s.next_mangled(options, this); if (this.global && cache) { cache.set(this.name, this.mangled_name); } } }
...
}
if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
to_mangle.push(node.definition());
return;
}
});
this.walk(tw);
to_mangle.forEach(function(def){ def.mangle(options) });
if (options.cache) {
options.cache.cname = this.cname;
}
});
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
...
unmangleable = function (options) { if (!options) options = {}; return (this.global && !options.toplevel) || this.undeclared || (!options.eval && (this.scope.uses_eval || this.scope.uses_with)) || (options.keep_fnames && (this.orig[0] instanceof AST_SymbolLambda || this.orig[0] instanceof AST_SymbolDefun)); }
...
|| this.orig[0] instanceof AST_SymbolDefun));
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
if (this.global && cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
}
else if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope;
var sym = this.orig[0];
if (!options.screw_ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
this.mangled_name = def.mangled_name || def.name;
...
function TreeWalker(callback) { this.visit = callback; this.stack = []; this.directives = Object.create(null); }
...
console.log("%s", JSON.stringify(output, null, 2));
}
function getProps(filename) {
var code = fs.readFileSync(filename, "utf8");
var ast = U2.parse(code);
ast.walk(new U2.TreeWalker(function(node){
if (node instanceof U2.AST_ObjectKeyVal) {
add(node.key);
}
else if (node instanceof U2.AST_ObjectProperty) {
add(node.key.name);
}
else if (node instanceof U2.AST_Dot) {
...
_visit = function (node, descend) { this.push(node); var ret = this.visit(node, descend ? function(){ descend.call(node); } : noop); if (!ret && descend) { descend.call(node); } this.pop(node); return ret; }
...
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
}, null);
AST_Node.warn_function = null;
...
find_parent = function (type) { var stack = this.stack; for (var i = stack.length; --i >= 0;) { var x = stack[i]; if (x instanceof type) return x; } }
...
});
def(AST_SymbolRef, function(compressor, suffix){
if (!this.global()) return;
var name;
var defines = compressor.option("global_defs");
if (defines && HOP(defines, (name = this.name + suffix))) {
var node = to_node(defines[name], this);
var top = compressor.find_parent(AST_Toplevel);
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) {
node.scope = top;
node.thedef = top.def_global(node);
}
}));
return node;
...
has_directive = function (type) { var dir = this.directives[type]; if (dir) return dir; var node = this.stack[this.stack.length - 1]; if (node instanceof AST_Scope) { for (var i = 0; i < node.body.length; ++i) { var st = node.body[i]; if (!(st instanceof AST_Directive)) break; if (st.value == type) return st; } } }
...
(function(){
function OPT(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
return opt;
});
};
OPT(AST_Node, function(self, compressor){
...
in_boolean_context = function () { var stack = this.stack; var i = stack.length, self = stack[--i]; while (i > 0) { var p = stack[--i]; if ((p instanceof AST_If && p.condition === self) || (p instanceof AST_Conditional && p.condition === self) || (p instanceof AST_DWLoop && p.condition === self) || (p instanceof AST_For && p.condition === self) || (p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self)) { return true; } if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||"))) return false; self = p; } }
...
if (e) {
self.expression = e;
return self;
} else {
return make_node(AST_Undefined, self).optimize(compressor);
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) {
switch (self.operator) {
case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
// !!foo ==> foo, if we're in boolean context
return e.expression;
}
if (e instanceof AST_Binary) {
...
loopcontrol_target = function (node) { var stack = this.stack; if (node.label) for (var i = stack.length; --i >= 0;) { var x = stack[i]; if (x instanceof AST_LabeledStatement && x.label.name == node.label.name) return x.body; } else for (var i = stack.length; --i >= 0;) { var x = stack[i]; if (x instanceof AST_IterationStatement || node instanceof AST_Break && x instanceof AST_Switch) return x; } }
...
}).transform(compressor));
ret.unshift(stat);
continue loop;
}
}
var ab = aborts(stat.body);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
...
parent = function (n) { return this.stack[this.stack.length - 2 - (n || 0)]; }
...
descend();
safe_ids = save_ids;
return true;
}
var iife;
if (node instanceof AST_Function
&& !node.name
&& (iife = tw.parent()) instanceof AST_Call
&& iife.expression === node) {
// Virtually turn IIFE parameters into variable definitions:
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
// So existing transformation rules can work on them.
node.argnames.forEach(function(arg, i) {
var d = arg.definition();
d.fixed = function() {
...
pop = function (node) { this.stack.pop(); if (node instanceof AST_Lambda) { this.directives = Object.getPrototypeOf(this.directives); } }
...
this.push(node);
var ret = this.visit(node, descend ? function(){
descend.call(node);
} : noop);
if (!ret && descend) {
descend.call(node);
}
this.pop(node);
return ret;
},
parent: function(n) {
return this.stack[this.stack.length - 2 - (n || 0)];
},
push: function (node) {
if (node instanceof AST_Lambda) {
...
push = function (node) { if (node instanceof AST_Lambda) { this.directives = Object.create(this.directives); } else if (node instanceof AST_Directive && !this.directives[node.value]) { this.directives[node.value] = node; } this.stack.push(node); }
...
code += "this.initialize();";
code += "}}";
var ctor = new Function(code)();
if (proto) {
ctor.prototype = proto;
ctor.BASE = base;
}
if (base) base.SUBCLASSES.push(ctor);
ctor.prototype.CTOR = ctor;
ctor.PROPS = props || null;
ctor.SELF_PROPS = self_props;
ctor.SUBCLASSES = [];
if (type) {
ctor.prototype.TYPE = ctor.TYPE = type;
}
...
self = function () { return this.stack[this.stack.length - 1]; }
...
function collapse_single_use_vars(statements, compressor) {
// Iterate statements backwards looking for a statement with a var/const
// declaration immediately preceding it. Grab the rightmost var definition
// and if it has exactly one reference then attempt to replace its reference
// in the statement with the var value and then erase the var definition.
var self = compressor.self();
var var_defs_removed = false;
var toplevel = compressor.option("toplevel");
for (var stat_index = statements.length; --stat_index >= 0;) {
var stat = statements[stat_index];
if (stat instanceof AST_Definitions) continue;
// Process child blocks of statement if present.
...
function base54(num) { var ret = "", base = 54; num++; do { num--; ret += String.fromCharCode(chars[num % base]); num = Math.floor(num / base); base = 64; } while (num > 0); return ret; }
n/a
consider = function (str){ for (var i = str.length; --i >= 0;) { var code = str.charCodeAt(i); if (code in frequency) ++frequency[code]; } }
...
}
});
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options);
var tw = new TreeWalker(function(node){
if (node instanceof AST_Constant)
base54.consider(node.print_to_string());
else if (node instanceof AST_Return)
base54.consider("return");
else if (node instanceof AST_Throw)
base54.consider("throw");
else if (node instanceof AST_Continue)
base54.consider("continue");
else if (node instanceof AST_Break)
...
freq = function (){ return frequency }
n/a
get = function (){ return chars }
...
self.walk(tw);
// pass 2: for every used symbol we need to walk its
// initialization code to figure out if it uses other
// symbols (that may not be in_use).
for (var i = 0; i < in_use.length; ++i) {
in_use[i].orig.forEach(function(decl){
// undeclared globals will be instanceof AST_SymbolRef
var init = initializations.get(decl.name);
if (init) init.forEach(function(init){
var tw = new TreeWalker(function(node){
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
...
function reset() { frequency = Object.create(null); chars = string.split("").map(function(ch){ return ch.charCodeAt(0) }); chars.forEach(function(ch){ frequency[ch] = 0 }); }
n/a
sort = function () { chars = mergeSort(chars, function(a, b){ if (is_digit(a) && !is_digit(b)) return 1; if (is_digit(b) && !is_digit(a)) return -1; return frequency[b] - frequency[a]; }); }
...
base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary)
base54.consider(node.operator);
else if (node instanceof AST_Dot)
base54.consider(node.property);
});
this.walk(tw);
base54.sort();
});
var base54 = (function() {
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
var chars, frequency;
function reset() {
frequency = Object.create(null);
...