From 9e19e63551907be19c8c6b5b75217d7650504436 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 6 Jun 2018 17:50:56 +0800 Subject: general clean-ups (#3175) --- lib/ast.js | 137 ++++++------ lib/compress.js | 597 ++++++++++++++++++++++++++--------------------------- lib/mozilla-ast.js | 35 ++-- lib/output.js | 230 +++++++++------------ lib/parse.js | 209 ++++++++++--------- lib/propmangle.js | 8 +- lib/scope.js | 24 +-- lib/sourcemap.js | 73 ++++--- lib/transform.js | 132 +++++------- lib/utils.js | 14 +- 10 files changed, 694 insertions(+), 765 deletions(-) (limited to 'lib') diff --git a/lib/ast.js b/lib/ast.js index 1a77a59d..c37ba598 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -44,21 +44,21 @@ "use strict"; function DEFNODE(type, props, methods, base) { - if (arguments.length < 4) base = AST_Node; - if (!props) props = []; - else props = props.split(/\s+/); + if (typeof base === "undefined") base = AST_Node; + props = props ? props.split(/\s+/) : []; var self_props = props; - if (base && base.PROPS) - 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] + ";"; - } + if (base && base.PROPS) props = props.concat(base.PROPS); + var code = [ + "return function AST_", type, "(props){", + "if(props){", + ]; + props.forEach(function(prop) { + code.push("this.", prop, "=props.", prop, ";"); + }); var proto = base && new base; - if (proto && proto.initialize || (methods && methods.initialize)) - code += "this.initialize();"; - code += "}}"; - var ctor = new Function(code)(); + if (proto && proto.initialize || methods && methods.initialize) code.push("this.initialize();"); + code.push("}}"); + var ctor = new Function(code.join(""))(); if (proto) { ctor.prototype = proto; ctor.BASE = base; @@ -71,11 +71,11 @@ function DEFNODE(type, props, methods, base) { if (type) { ctor.prototype.TYPE = ctor.TYPE = type; } - if (methods) for (i in methods) if (HOP(methods, i)) { - if (/^\$/.test(i)) { - ctor[i.substr(1)] = methods[i]; + if (methods) for (var name in methods) if (HOP(methods, name)) { + if (/^\$/.test(name)) { + ctor[name.substr(1)] = methods[name]; } else { - ctor.prototype[i] = methods[i]; + ctor.prototype[name] = methods[name]; } } ctor.DEFMETHOD = function(name, method) { @@ -85,7 +85,7 @@ function DEFNODE(type, props, methods, base) { exports["AST_" + type] = ctor; } return ctor; -}; +} var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before comments_after file raw", { }, null); @@ -148,7 +148,7 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", { body: "[AST_Node] an expression node (should not be instanceof AST_Statement)" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.body._walk(visitor); }); } @@ -158,11 +158,10 @@ function walk_body(node, visitor) { var body = node.body; if (body instanceof AST_Statement) { body._walk(visitor); - } - else for (var i = 0, len = body.length; i < len; i++) { - body[i]._walk(visitor); - } -}; + } else body.forEach(function(node) { + node._walk(visitor); + }); +} var AST_Block = DEFNODE("Block", "body", { $documentation: "A body of statements (usually braced)", @@ -170,7 +169,7 @@ var AST_Block = DEFNODE("Block", "body", { body: "[AST_Statement*] an array of statements" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { walk_body(this, visitor); }); } @@ -197,7 +196,7 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", { label: "[AST_Label] a label definition" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.label._walk(visitor); this.body._walk(visitor); }); @@ -233,7 +232,7 @@ var AST_DWLoop = DEFNODE("DWLoop", "condition", { var AST_Do = DEFNODE("Do", null, { $documentation: "A `do` statement", _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.body._walk(visitor); this.condition._walk(visitor); }); @@ -243,7 +242,7 @@ var AST_Do = DEFNODE("Do", null, { var AST_While = DEFNODE("While", null, { $documentation: "A `while` statement", _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.condition._walk(visitor); this.body._walk(visitor); }); @@ -258,7 +257,7 @@ var AST_For = DEFNODE("For", "init condition step", { step: "[AST_Node?] the `for` update clause, or null if empty" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + 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); @@ -274,7 +273,7 @@ var AST_ForIn = DEFNODE("ForIn", "init object", { object: "[AST_Node] the object that we're looping through" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.init._walk(visitor); this.object._walk(visitor); this.body._walk(visitor); @@ -288,7 +287,7 @@ var AST_With = DEFNODE("With", "expression", { expression: "[AST_Node] the `with` expression" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.expression._walk(visitor); this.body._walk(visitor); }); @@ -329,7 +328,7 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", { var body = this.body; var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");"; wrapped_tl = parse(wrapped_tl); - wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ + wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node) { if (node instanceof AST_Directive && node.value == "$ORIG") { return MAP.splice(body); } @@ -363,12 +362,11 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", { uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + 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); - } + this.argnames.forEach(function(argname) { + argname._walk(visitor); + }); walk_body(this, visitor); }); } @@ -398,7 +396,7 @@ var AST_Exit = DEFNODE("Exit", "value", { value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return" }, _walk: function(visitor) { - return visitor._visit(this, this.value && function(){ + return visitor._visit(this, this.value && function() { this.value._walk(visitor); }); } @@ -418,7 +416,7 @@ var AST_LoopControl = DEFNODE("LoopControl", "label", { label: "[AST_LabelRef?] the label, or null if none", }, _walk: function(visitor) { - return visitor._visit(this, this.label && function(){ + return visitor._visit(this, this.label && function() { this.label._walk(visitor); }); } @@ -441,7 +439,7 @@ var AST_If = DEFNODE("If", "condition alternative", { alternative: "[AST_Statement?] the `else` part, or null if not present" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.condition._walk(visitor); this.body._walk(visitor); if (this.alternative) this.alternative._walk(visitor); @@ -457,7 +455,7 @@ var AST_Switch = DEFNODE("Switch", "expression", { expression: "[AST_Node] the `switch` “discriminant”" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.expression._walk(visitor); walk_body(this, visitor); }); @@ -478,7 +476,7 @@ var AST_Case = DEFNODE("Case", "expression", { expression: "[AST_Node] the `case` expression" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.expression._walk(visitor); walk_body(this, visitor); }); @@ -494,7 +492,7 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", { bfinally: "[AST_Finally?] the finally block, or null if not present" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { walk_body(this, visitor); if (this.bcatch) this.bcatch._walk(visitor); if (this.bfinally) this.bfinally._walk(visitor); @@ -508,7 +506,7 @@ var AST_Catch = DEFNODE("Catch", "argname", { argname: "[AST_SymbolCatch] symbol for the exception" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.argname._walk(visitor); walk_body(this, visitor); }); @@ -527,11 +525,10 @@ var AST_Definitions = DEFNODE("Definitions", "definitions", { definitions: "[AST_VarDef*] array of variable definitions" }, _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); - } + return visitor._visit(this, function() { + this.definitions.forEach(function(defn) { + defn._walk(visitor); + }); }); } }, AST_Statement); @@ -547,7 +544,7 @@ var AST_VarDef = DEFNODE("VarDef", "name value", { value: "[AST_Node?] initializer, or null of there's no initializer" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.name._walk(visitor); if (this.value) this.value._walk(visitor); }); @@ -582,7 +579,7 @@ var AST_Sequence = DEFNODE("Sequence", "expressions", { expressions: "[AST_Node*] array of expressions (at least two)" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.expressions.forEach(function(node) { node._walk(visitor); }); @@ -601,7 +598,7 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", { var AST_Dot = DEFNODE("Dot", null, { $documentation: "A dotted property access expression", _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.expression._walk(visitor); }); } @@ -610,7 +607,7 @@ var AST_Dot = DEFNODE("Dot", null, { var AST_Sub = DEFNODE("Sub", null, { $documentation: "Index-style property access, i.e. `a[\"foo\"]`", _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.expression._walk(visitor); this.property._walk(visitor); }); @@ -624,7 +621,7 @@ var AST_Unary = DEFNODE("Unary", "operator expression", { expression: "[AST_Node] expression that this unary operator applies to" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.expression._walk(visitor); }); } @@ -646,7 +643,7 @@ var AST_Binary = DEFNODE("Binary", "operator left right", { right: "[AST_Node] right-hand side expression" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.left._walk(visitor); this.right._walk(visitor); }); @@ -661,7 +658,7 @@ var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", alternative: "[AST_Node]" }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.condition._walk(visitor); this.consequent._walk(visitor); this.alternative._walk(visitor); @@ -681,11 +678,10 @@ var AST_Array = DEFNODE("Array", "elements", { elements: "[AST_Node*] array of elements" }, _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); - } + return visitor._visit(this, function() { + this.elements.forEach(function(element) { + element._walk(visitor); + }); }); } }); @@ -696,11 +692,10 @@ var AST_Object = DEFNODE("Object", "properties", { properties: "[AST_ObjectProperty*] array of properties" }, _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); - } + return visitor._visit(this, function() { + this.properties.forEach(function(prop) { + prop._walk(visitor); + }); }); } }); @@ -712,7 +707,7 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { value: "[AST_Node] property value. For getters and setters this is an AST_Accessor." }, _walk: function(visitor) { - return visitor._visit(this, function(){ + return visitor._visit(this, function() { this.value._walk(visitor); }); } @@ -839,12 +834,12 @@ var AST_NaN = DEFNODE("NaN", null, { var AST_Undefined = DEFNODE("Undefined", null, { $documentation: "The `undefined` value", - value: (function(){}()) + value: function(){}() }, AST_Atom); var AST_Hole = DEFNODE("Hole", null, { $documentation: "A hole in an array", - value: (function(){}()) + value: function(){}() }, AST_Atom); var AST_Infinity = DEFNODE("Infinity", null, { @@ -872,11 +867,11 @@ function TreeWalker(callback) { this.visit = callback; this.stack = []; this.directives = Object.create(null); -}; +} TreeWalker.prototype = { _visit: function(node, descend) { this.push(node); - var ret = this.visit(node, descend ? function(){ + var ret = this.visit(node, descend ? function() { descend.call(node); } : noop); if (!ret && descend) { diff --git a/lib/compress.js b/lib/compress.js index 1832f7a8..ad01e48c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -136,7 +136,7 @@ function Compressor(options, false_by_default) { var sequences = this.options["sequences"]; this.sequences_limit = sequences == 1 ? 800 : sequences | 0; this.warnings_produced = {}; -}; +} Compressor.prototype = new TreeTransformer; merge(Compressor.prototype, { @@ -201,11 +201,10 @@ merge(Compressor.prototype, { }, before: function(node, descend, in_list) { if (node._squeezed) return node; - var was_scope = false; - if (node instanceof AST_Scope) { - node = node.hoist_properties(this); - node = node.hoist_declarations(this); - was_scope = true; + var is_scope = node instanceof AST_Scope; + if (is_scope) { + node.hoist_properties(this); + node.hoist_declarations(this); } // Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize() // would call AST_Node.transform() if a different instance of AST_Node is @@ -220,7 +219,7 @@ merge(Compressor.prototype, { // output and performance. descend(node, this); var opt = node.optimize(this); - if (was_scope && opt instanceof AST_Scope) { + if (is_scope) { opt.drop_unused(this); descend(opt, this); } @@ -229,24 +228,12 @@ merge(Compressor.prototype, { } }); -(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){ +(function(OPT) { + OPT(AST_Node, function(self, compressor) { return self; }); - AST_Node.DEFMETHOD("equivalent_to", function(node){ + AST_Node.DEFMETHOD("equivalent_to", function(node) { return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string(); }); @@ -821,7 +808,7 @@ merge(Compressor.prototype, { tw.in_loop = saved_loop; return true; }); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("reduce_vars", func); }); @@ -894,7 +881,7 @@ merge(Compressor.prototype, { if (!props.end) props.end = orig.end; } return new ctor(props); - }; + } function make_sequence(orig, expressions) { if (expressions.length == 1) return expressions[0]; @@ -971,21 +958,21 @@ merge(Compressor.prototype, { if (thing instanceof AST_EmptyStatement) return []; if (thing instanceof AST_Statement) return [ thing ]; throw new Error("Can't convert thing to statement array"); - }; + } function is_empty(thing) { if (thing === null) return true; if (thing instanceof AST_EmptyStatement) return true; if (thing instanceof AST_BlockStatement) return thing.body.length == 0; return false; - }; + } function loop_body(x) { if (x instanceof AST_IterationStatement) { return x.body instanceof AST_BlockStatement ? x.body : x; } return x; - }; + } function root_expr(prop) { while (prop instanceof AST_PropAccess) prop = prop.expression; @@ -1406,7 +1393,10 @@ merge(Compressor.prototype, { } } else if (expr instanceof AST_VarDef) { if (expr.value) { - candidates.push(hit_stack.slice()); + var def = expr.name.definition(); + if (def.references.length > def.replaced) { + candidates.push(hit_stack.slice()); + } extract_candidates(expr.value); } } @@ -1476,7 +1466,6 @@ merge(Compressor.prototype, { var def = expr.name.definition(); if (!member(expr.name, def.orig)) return; var referenced = def.references.length - def.replaced; - if (!referenced) return; var declared = def.orig.length - def.eliminated; if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg) || (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) { @@ -1947,7 +1936,7 @@ merge(Compressor.prototype, { CHANGED = true; var left = prev.body; return make_sequence(left, [ left, right ]).transform(compressor); - }; + } var n = 0, prev; for (var i = 0; i < statements.length; i++) { var stat = statements[i]; @@ -2149,7 +2138,7 @@ merge(Compressor.prototype, { if (!(stat instanceof AST_Defun)) { compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start); } - stat.walk(new TreeWalker(function(node){ + 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(); @@ -2164,7 +2153,7 @@ merge(Compressor.prototype, { return true; } })); - }; + } function get_value(key) { if (key instanceof AST_Constant) { @@ -2272,83 +2261,83 @@ merge(Compressor.prototype, { /* -----[ boolean/negation helpers ]----- */ // methods to determine whether an expression has a boolean result type - (function(def){ + (function(def) { var unary_bool = makePredicate("! delete"); var binary_bool = makePredicate("in instanceof == != === !== < <= >= >"); def(AST_Node, return_false); - def(AST_UnaryPrefix, function(){ + def(AST_UnaryPrefix, function() { return unary_bool[this.operator]; }); - def(AST_Binary, function(){ + def(AST_Binary, function() { return binary_bool[this.operator] || lazy_op[this.operator] && this.left.is_boolean() && this.right.is_boolean(); }); - def(AST_Conditional, function(){ + def(AST_Conditional, function() { return this.consequent.is_boolean() && this.alternative.is_boolean(); }); - def(AST_Assign, function(){ + def(AST_Assign, function() { return this.operator == "=" && this.right.is_boolean(); }); - def(AST_Sequence, function(){ + def(AST_Sequence, function() { return this.tail_node().is_boolean(); }); def(AST_True, return_true); def(AST_False, return_true); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("is_boolean", func); }); // methods to determine if an expression has a numeric result type - (function(def){ + (function(def) { def(AST_Node, return_false); def(AST_Number, return_true); var unary = makePredicate("+ - ~ ++ --"); - def(AST_Unary, function(){ + def(AST_Unary, function() { return unary[this.operator]; }); var binary = makePredicate("- * / % & | ^ << >> >>>"); - def(AST_Binary, function(compressor){ + 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){ + def(AST_Assign, function(compressor) { return binary[this.operator.slice(0, -1)] || this.operator == "=" && this.right.is_number(compressor); }); - def(AST_Sequence, function(compressor){ + def(AST_Sequence, function(compressor) { return this.tail_node().is_number(compressor); }); - def(AST_Conditional, function(compressor){ + def(AST_Conditional, function(compressor) { return this.consequent.is_number(compressor) && this.alternative.is_number(compressor); }); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("is_number", func); }); // methods to determine if an expression has a string result type - (function(def){ + (function(def) { def(AST_Node, return_false); def(AST_String, return_true); - def(AST_UnaryPrefix, function(){ + def(AST_UnaryPrefix, function() { return this.operator == "typeof"; }); - def(AST_Binary, function(compressor){ + def(AST_Binary, function(compressor) { return this.operator == "+" && (this.left.is_string(compressor) || this.right.is_string(compressor)); }); - def(AST_Assign, function(compressor){ + def(AST_Assign, function(compressor) { return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor); }); - def(AST_Sequence, function(compressor){ + def(AST_Sequence, function(compressor) { return this.tail_node().is_string(compressor); }); - def(AST_Conditional, function(compressor){ + def(AST_Conditional, function(compressor) { return this.consequent.is_string(compressor) && this.alternative.is_string(compressor); }); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("is_string", func); }); @@ -2360,7 +2349,7 @@ merge(Compressor.prototype, { if (parent instanceof AST_Assign && parent.left === node) return node; } - (function(def){ + (function(def) { AST_Node.DEFMETHOD("resolve_defines", function(compressor) { if (!compressor.option("global_defs")) return; var def = this._find_defs(compressor, ""); @@ -2399,10 +2388,10 @@ merge(Compressor.prototype, { return make_node_from_constant(value, orig); } def(AST_Node, noop); - def(AST_Dot, function(compressor, suffix){ + def(AST_Dot, function(compressor, suffix) { return this.expression._find_defs(compressor, "." + this.property + suffix); }); - def(AST_SymbolRef, function(compressor, suffix){ + def(AST_SymbolRef, function(compressor, suffix) { if (!this.global()) return; var name; var defines = compressor.option("global_defs"); @@ -2418,7 +2407,7 @@ merge(Compressor.prototype, { return node; } }); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("_find_defs", func); }); @@ -2533,13 +2522,13 @@ merge(Compressor.prototype, { convert_to_predicate(static_fns); // methods to evaluate a constant expression - (function(def){ + (function(def) { // If the node has been successfully reduced to a 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){ + AST_Node.DEFMETHOD("evaluate", function(compressor) { if (!compressor.option("evaluate")) return this; var cached = []; var val = this._eval(compressor, cached, 1); @@ -2551,7 +2540,7 @@ merge(Compressor.prototype, { return val; }); var unaryPrefix = makePredicate("! ~ - + void"); - AST_Node.DEFMETHOD("is_constant", function(){ + AST_Node.DEFMETHOD("is_constant", function() { // Accomodate when compress option evaluate=false // as well as the common constant expressions !0 and -1 if (this instanceof AST_Constant) { @@ -2562,12 +2551,12 @@ merge(Compressor.prototype, { && unaryPrefix[this.operator]; } }); - def(AST_Statement, function(){ + def(AST_Statement, function() { throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start)); }); def(AST_Lambda, return_this); def(AST_Node, return_this); - def(AST_Constant, function(){ + def(AST_Constant, function() { return this.getValue(); }); def(AST_Function, function(compressor) { @@ -2811,12 +2800,12 @@ merge(Compressor.prototype, { return this; }); def(AST_New, return_this); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("_eval", func); }); // method to negate an expression - (function(def){ + (function(def) { function basic_negation(exp) { return make_node(AST_UnaryPrefix, exp, { operator: "!", @@ -2833,32 +2822,32 @@ merge(Compressor.prototype, { } return best_of_expression(negated, alt); } - def(AST_Node, function(){ + def(AST_Node, function() { return basic_negation(this); }); - def(AST_Statement, function(){ + def(AST_Statement, function() { throw new Error("Cannot negate a statement"); }); - def(AST_Function, function(){ + def(AST_Function, function() { return basic_negation(this); }); - def(AST_UnaryPrefix, function(){ + def(AST_UnaryPrefix, function() { if (this.operator == "!") return this.expression; return basic_negation(this); }); - def(AST_Sequence, function(compressor){ + def(AST_Sequence, function(compressor) { var expressions = this.expressions.slice(); expressions.push(expressions.pop().negate(compressor)); return make_sequence(this, expressions); }); - def(AST_Conditional, function(compressor, first_in_statement){ + def(AST_Conditional, function(compressor, first_in_statement) { var self = this.clone(); self.consequent = self.consequent.negate(compressor); self.alternative = self.alternative.negate(compressor); return best(this, self, first_in_statement); }); - def(AST_Binary, function(compressor, first_in_statement){ + def(AST_Binary, function(compressor, first_in_statement) { var self = this.clone(), op = this.operator; if (compressor.option("unsafe_comps")) { switch (op) { @@ -2886,8 +2875,8 @@ merge(Compressor.prototype, { } return basic_negation(this); }); - })(function(node, func){ - node.DEFMETHOD("negate", function(compressor, first_in_statement){ + })(function(node, func) { + node.DEFMETHOD("negate", function(compressor, first_in_statement) { return func.call(this, compressor, first_in_statement); }); }); @@ -2926,7 +2915,7 @@ merge(Compressor.prototype, { }); // determine if expression has side effects - (function(def){ + (function(def) { def(AST_Node, return_true); def(AST_EmptyStatement, return_false); @@ -2940,10 +2929,10 @@ merge(Compressor.prototype, { return false; } - def(AST_Block, function(compressor){ + def(AST_Block, function(compressor) { return any(this.body, compressor); }); - def(AST_Call, function(compressor){ + def(AST_Call, function(compressor) { if (!this.is_expr_pure(compressor) && (!this.expression.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) { @@ -2951,82 +2940,82 @@ merge(Compressor.prototype, { } return any(this.args, compressor); }); - def(AST_Switch, function(compressor){ + def(AST_Switch, function(compressor) { return this.expression.has_side_effects(compressor) || any(this.body, compressor); }); - def(AST_Case, function(compressor){ + def(AST_Case, function(compressor) { return this.expression.has_side_effects(compressor) || any(this.body, compressor); }); - def(AST_Try, function(compressor){ + def(AST_Try, function(compressor) { return any(this.body, compressor) || this.bcatch && this.bcatch.has_side_effects(compressor) || this.bfinally && this.bfinally.has_side_effects(compressor); }); - def(AST_If, function(compressor){ + def(AST_If, 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); }); - def(AST_LabeledStatement, function(compressor){ + def(AST_LabeledStatement, function(compressor) { return this.body.has_side_effects(compressor); }); - def(AST_SimpleStatement, function(compressor){ + def(AST_SimpleStatement, function(compressor) { return this.body.has_side_effects(compressor); }); def(AST_Lambda, return_false); - def(AST_Binary, function(compressor){ + def(AST_Binary, function(compressor) { return this.left.has_side_effects(compressor) || this.right.has_side_effects(compressor); }); def(AST_Assign, return_true); - def(AST_Conditional, function(compressor){ + def(AST_Conditional, function(compressor) { return this.condition.has_side_effects(compressor) || this.consequent.has_side_effects(compressor) || this.alternative.has_side_effects(compressor); }); - def(AST_Unary, function(compressor){ + def(AST_Unary, function(compressor) { return unary_side_effects[this.operator] || this.expression.has_side_effects(compressor); }); - def(AST_SymbolRef, function(compressor){ + def(AST_SymbolRef, function(compressor) { return !this.is_declared(compressor); }); def(AST_SymbolDeclaration, return_false); - def(AST_Object, function(compressor){ + def(AST_Object, function(compressor) { return any(this.properties, compressor); }); - def(AST_ObjectProperty, function(compressor){ + def(AST_ObjectProperty, function(compressor) { return this.value.has_side_effects(compressor); }); - def(AST_Array, function(compressor){ + def(AST_Array, function(compressor) { return any(this.elements, compressor); }); - def(AST_Dot, function(compressor){ + def(AST_Dot, function(compressor) { return this.expression.may_throw_on_access(compressor) || this.expression.has_side_effects(compressor); }); - def(AST_Sub, function(compressor){ + def(AST_Sub, function(compressor) { return this.expression.may_throw_on_access(compressor) || this.expression.has_side_effects(compressor) || this.property.has_side_effects(compressor); }); - def(AST_Sequence, function(compressor){ + def(AST_Sequence, function(compressor) { return any(this.expressions, compressor); }); - def(AST_Definitions, function(compressor){ + def(AST_Definitions, function(compressor) { return any(this.definitions, compressor); }); - def(AST_VarDef, function(compressor){ + def(AST_VarDef, function(compressor) { return this.value; }); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("has_side_effects", func); }); // determine if expression may throw - (function(def){ + (function(def) { def(AST_Node, return_true); def(AST_Constant, return_false); @@ -3042,10 +3031,10 @@ merge(Compressor.prototype, { return false; } - def(AST_Array, function(compressor){ + def(AST_Array, function(compressor) { return any(this.elements, compressor); }); - def(AST_Assign, function(compressor){ + def(AST_Assign, function(compressor) { if (this.right.may_throw(compressor)) return true; if (!compressor.has_directive("use strict") && this.operator == "=" @@ -3054,90 +3043,90 @@ merge(Compressor.prototype, { } return this.left.may_throw(compressor); }); - def(AST_Binary, function(compressor){ + def(AST_Binary, function(compressor) { return this.left.may_throw(compressor) || this.right.may_throw(compressor); }); - def(AST_Block, function(compressor){ + def(AST_Block, function(compressor) { return any(this.body, compressor); }); - def(AST_Call, function(compressor){ + def(AST_Call, function(compressor) { if (any(this.args, compressor)) return true; if (this.is_expr_pure(compressor)) return false; if (this.expression.may_throw(compressor)) return true; return !(this.expression instanceof AST_Lambda) || any(this.expression.body, compressor); }); - def(AST_Case, function(compressor){ + def(AST_Case, function(compressor) { return this.expression.may_throw(compressor) || any(this.body, compressor); }); - def(AST_Conditional, function(compressor){ + def(AST_Conditional, function(compressor) { return this.condition.may_throw(compressor) || this.consequent.may_throw(compressor) || this.alternative.may_throw(compressor); }); - def(AST_Definitions, function(compressor){ + def(AST_Definitions, function(compressor) { return any(this.definitions, compressor); }); - def(AST_Dot, function(compressor){ + def(AST_Dot, function(compressor) { return this.expression.may_throw_on_access(compressor) || this.expression.may_throw(compressor); }); - def(AST_If, function(compressor){ + def(AST_If, function(compressor) { return this.condition.may_throw(compressor) || this.body && this.body.may_throw(compressor) || this.alternative && this.alternative.may_throw(compressor); }); - def(AST_LabeledStatement, function(compressor){ + def(AST_LabeledStatement, function(compressor) { return this.body.may_throw(compressor); }); - def(AST_Object, function(compressor){ + def(AST_Object, function(compressor) { return any(this.properties, compressor); }); - def(AST_ObjectProperty, function(compressor){ + def(AST_ObjectProperty, function(compressor) { return this.value.may_throw(compressor); }); - def(AST_Return, function(compressor){ + def(AST_Return, function(compressor) { return this.value && this.value.may_throw(compressor); }); - def(AST_Sequence, function(compressor){ + def(AST_Sequence, function(compressor) { return any(this.expressions, compressor); }); - def(AST_SimpleStatement, function(compressor){ + def(AST_SimpleStatement, function(compressor) { return this.body.may_throw(compressor); }); - def(AST_Sub, function(compressor){ + def(AST_Sub, function(compressor) { return this.expression.may_throw_on_access(compressor) || this.expression.may_throw(compressor) || this.property.may_throw(compressor); }); - def(AST_Switch, function(compressor){ + def(AST_Switch, function(compressor) { return this.expression.may_throw(compressor) || any(this.body, compressor); }); - def(AST_SymbolRef, function(compressor){ + def(AST_SymbolRef, function(compressor) { return !this.is_declared(compressor); }); - def(AST_Try, function(compressor){ + def(AST_Try, function(compressor) { return this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor) || this.bfinally && this.bfinally.may_throw(compressor); }); - def(AST_Unary, function(compressor){ + def(AST_Unary, function(compressor) { if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) return false; return this.expression.may_throw(compressor); }); - def(AST_VarDef, function(compressor){ + def(AST_VarDef, function(compressor) { if (!this.value) return false; return this.value.may_throw(compressor); }); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("may_throw", func); }); // determine if expression is constant - (function(def){ + (function(def) { function all(list) { for (var i = list.length; --i >= 0;) if (!list[i].is_constant_expression()) @@ -3146,7 +3135,7 @@ merge(Compressor.prototype, { } def(AST_Node, return_false); def(AST_Constant, return_true); - def(AST_Lambda, function(scope){ + def(AST_Lambda, function(scope) { var self = this; var result = true; self.walk(new TreeWalker(function(node) { @@ -3173,61 +3162,61 @@ merge(Compressor.prototype, { })); return result; }); - def(AST_Unary, function(){ + def(AST_Unary, function() { return this.expression.is_constant_expression(); }); - def(AST_Binary, function(){ + def(AST_Binary, function() { return this.left.is_constant_expression() && this.right.is_constant_expression(); }); - def(AST_Array, function(){ + def(AST_Array, function() { return all(this.elements); }); - def(AST_Object, function(){ + def(AST_Object, function() { return all(this.properties); }); - def(AST_ObjectProperty, function(){ + def(AST_ObjectProperty, function() { return this.value.is_constant_expression(); }); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("is_constant_expression", func); }); // tell me if a statement aborts function aborts(thing) { return thing && thing.aborts(); - }; - (function(def){ + } + (function(def) { def(AST_Statement, return_null); def(AST_Jump, return_this); - function block_aborts(){ + function block_aborts() { var n = this.body.length; return n > 0 && aborts(this.body[n - 1]); - }; + } def(AST_BlockStatement, block_aborts); def(AST_SwitchBranch, block_aborts); - def(AST_If, function(){ + def(AST_If, function() { return this.alternative && aborts(this.body) && aborts(this.alternative) && this; }); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("aborts", func); }); /* -----[ optimizers ]----- */ - OPT(AST_Directive, function(self, compressor){ + OPT(AST_Directive, function(self, compressor) { if (compressor.has_directive(self.value) !== self) { return make_node(AST_EmptyStatement, self); } return self; }); - OPT(AST_Debugger, function(self, compressor){ + OPT(AST_Debugger, function(self, compressor) { if (compressor.option("drop_debugger")) return make_node(AST_EmptyStatement, self); return self; }); - OPT(AST_LabeledStatement, function(self, compressor){ + OPT(AST_LabeledStatement, function(self, compressor) { if (self.body instanceof AST_Break && compressor.loopcontrol_target(self.body) === self.body) { return make_node(AST_EmptyStatement, self); @@ -3235,12 +3224,12 @@ merge(Compressor.prototype, { return self.label.references.length == 0 ? self.body : self; }); - OPT(AST_Block, function(self, compressor){ + OPT(AST_Block, function(self, compressor) { tighten_body(self.body, compressor); return self; }); - OPT(AST_BlockStatement, function(self, compressor){ + OPT(AST_BlockStatement, function(self, compressor) { tighten_body(self.body, compressor); switch (self.body.length) { case 1: return self.body[0]; @@ -3249,7 +3238,7 @@ merge(Compressor.prototype, { return self; }); - OPT(AST_Lambda, function(self, compressor){ + OPT(AST_Lambda, function(self, compressor) { tighten_body(self.body, compressor); if (compressor.option("side_effects") && self.body.length == 1 @@ -3259,7 +3248,7 @@ merge(Compressor.prototype, { return self; }); - AST_Scope.DEFMETHOD("drop_unused", function(compressor){ + AST_Scope.DEFMETHOD("drop_unused", function(compressor) { if (!compressor.option("unused")) return; if (compressor.has_directive("use asm")) return; var self = this; @@ -3299,7 +3288,7 @@ merge(Compressor.prototype, { // 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){ + var tw = new TreeWalker(function(node, descend) { if (node === self) return; if (node instanceof AST_Defun) { var node_def = node.name.definition(); @@ -3316,7 +3305,7 @@ merge(Compressor.prototype, { var_defs_by_id.add(node.definition().id, node); } if (node instanceof AST_Definitions && scope === self) { - node.definitions.forEach(function(def){ + node.definitions.forEach(function(def) { var node_def = def.name.definition(); if (def.name instanceof AST_SymbolVar) { var_defs_by_id.add(node_def.id, def); @@ -3607,135 +3596,128 @@ merge(Compressor.prototype, { } }); - AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){ - var self = this; - if (compressor.has_directive("use asm")) return self; + AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) { + if (compressor.has_directive("use asm")) return; 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; + var self = this; + if (hoist_vars) { // 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; + var var_decl = 0; + self.walk(new TreeWalker(function(node) { + if (var_decl > 1) return true; + if (node instanceof AST_Scope && node !== self) return true; if (node instanceof AST_Var) { - ++var_decl; + 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 (hoist_funs && node instanceof AST_Defun - && (tt.parent() === self || !compressor.has_directive("use strict"))) { - hoisted.push(node); - return make_node(AST_EmptyStatement, node); - } - if (hoist_vars && node instanceof AST_Var) { - 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 - && !all(self.argnames, function(argname) { - return argname.name != name; - })) { - vars.del(name); - } else { - def = def.clone(); - def.value = null; - defs.push(def); - vars.set(name, def); - } + if (var_decl <= 1) hoist_vars = false; + } + if (!hoist_funs && !hoist_vars) return; + var dirs = []; + var hoisted = []; + var vars = new Dictionary(), vars_found = 0; + var tt = new TreeTransformer(function(node) { + if (node === self) return; + if (node instanceof AST_Directive) { + dirs.push(node); + return make_node(AST_EmptyStatement, node); + } + if (hoist_funs && node instanceof AST_Defun + && (tt.parent() === self || !compressor.has_directive("use strict"))) { + hoisted.push(node); + return make_node(AST_EmptyStatement, node); + } + if (hoist_vars && node instanceof AST_Var) { + node.definitions.forEach(function(def) { + vars.set(def.name.name, def); + ++vars_found; }); - 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; - } - if (expr instanceof AST_Sequence - && (assign = expr.expressions[0]) instanceof AST_Assign - && assign.operator == "=" - && (sym = assign.left) instanceof AST_Symbol - && vars.has(sym.name)) - { - var def = vars.get(sym.name); - if (def.value) break; - def.value = assign.right; - remove(defs, def); - defs.push(def); - self.body[i].body = make_sequence(expr, expr.expressions.slice(1)); - continue; - } - } - if (self.body[i] instanceof AST_EmptyStatement) { + var seq = node.to_assignments(compressor); + var p = tt.parent(); + if (p instanceof AST_ForIn && p.init === node) { + if (seq) return seq; + var def = node.definitions[0].name; + return make_node(AST_SymbolRef, def, def); + } + 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; + }); + 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 + && !all(self.argnames, function(argname) { + return argname.name != name; + })) { + 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; } - if (self.body[i] instanceof AST_BlockStatement) { - var tmp = [ i, 1 ].concat(self.body[i].body); - self.body.splice.apply(self.body, tmp); + if (expr instanceof AST_Sequence + && (assign = expr.expressions[0]) instanceof AST_Assign + && assign.operator == "=" + && (sym = assign.left) instanceof AST_Symbol + && vars.has(sym.name)) + { + var def = vars.get(sym.name); + if (def.value) break; + def.value = assign.right; + remove(defs, def); + defs.push(def); + self.body[i].body = make_sequence(expr, expr.expressions.slice(1)); continue; } - break; } - defs = make_node(AST_Var, self, { - definitions: defs - }); - hoisted.push(defs); - }; + if (self.body[i] instanceof AST_EmptyStatement) { + self.body.splice(i, 1); + continue; + } + if (self.body[i] instanceof AST_BlockStatement) { + var tmp = [ i, 1 ].concat(self.body[i].body); + self.body.splice.apply(self.body, tmp); + continue; + } + break; + } + defs = make_node(AST_Var, self, { + definitions: defs + }); + hoisted.push(defs); } - self.body = dirs.concat(hoisted, self.body); } - return self; + self.body = dirs.concat(hoisted, self.body); }); AST_Scope.DEFMETHOD("var_names", function() { @@ -3761,12 +3743,12 @@ merge(Compressor.prototype, { return name; }); - AST_Scope.DEFMETHOD("hoist_properties", function(compressor){ + AST_Scope.DEFMETHOD("hoist_properties", function(compressor) { + if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return; var self = this; - if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return self; var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false; var defs_by_id = Object.create(null); - return self.transform(new TreeTransformer(function(node, descend) { + self.transform(new TreeTransformer(function(node, descend) { if (node instanceof AST_Assign && node.operator == "=" && node.write_only @@ -3832,6 +3814,7 @@ merge(Compressor.prototype, { if (def.assignments != count) return; if (def.direct_access) return; if (def.escaped == 1) return; + if (def.references.length == count) return; if (def.single_use) return; if (top_retain(def)) return; if (sym.fixed_value() !== right) return; @@ -3853,7 +3836,7 @@ merge(Compressor.prototype, { // drop_side_effect_free() // remove side-effect-free parts which only affects return value - (function(def){ + (function(def) { // Drop side-effect-free elements from an array of expressions. // Returns an array of expressions with side-effects or null // if all elements were dropped. Note: original array may be @@ -3876,7 +3859,7 @@ merge(Compressor.prototype, { def(AST_Node, return_this); def(AST_Constant, return_null); def(AST_This, return_null); - def(AST_Call, function(compressor, first_in_statement){ + def(AST_Call, function(compressor, first_in_statement) { if (!this.is_expr_pure(compressor)) { if (this.expression.is_call_pure(compressor)) { var exprs = this.args.slice(); @@ -3908,7 +3891,7 @@ merge(Compressor.prototype, { }); def(AST_Accessor, return_null); def(AST_Function, return_null); - def(AST_Binary, function(compressor, first_in_statement){ + def(AST_Binary, 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); if (lazy_op[this.operator]) { @@ -3922,7 +3905,7 @@ merge(Compressor.prototype, { return make_sequence(this, [ left, right ]); } }); - def(AST_Assign, function(compressor){ + def(AST_Assign, function(compressor) { var left = this.left; if (left.has_side_effects(compressor) || compressor.has_directive("use strict") @@ -3936,7 +3919,7 @@ merge(Compressor.prototype, { } return this; }); - def(AST_Conditional, function(compressor){ + def(AST_Conditional, 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; @@ -3955,7 +3938,7 @@ merge(Compressor.prototype, { node.alternative = alternative; return node; }); - def(AST_Unary, function(compressor, first_in_statement){ + def(AST_Unary, function(compressor, first_in_statement) { if (unary_side_effects[this.operator]) { this.write_only = !this.expression.has_side_effects(compressor); return this; @@ -3971,22 +3954,22 @@ merge(Compressor.prototype, { def(AST_SymbolRef, function(compressor) { return this.is_declared(compressor) ? null : this; }); - def(AST_Object, function(compressor, first_in_statement){ + def(AST_Object, function(compressor, first_in_statement) { var values = trim(this.properties, compressor, first_in_statement); return values && make_sequence(this, values); }); - def(AST_ObjectProperty, function(compressor, first_in_statement){ + def(AST_ObjectProperty, function(compressor, first_in_statement) { return this.value.drop_side_effect_free(compressor, first_in_statement); }); - def(AST_Array, function(compressor, first_in_statement){ + def(AST_Array, function(compressor, first_in_statement) { var values = trim(this.elements, compressor, first_in_statement); return values && make_sequence(this, values); }); - def(AST_Dot, function(compressor, first_in_statement){ + def(AST_Dot, function(compressor, first_in_statement) { if (this.expression.may_throw_on_access(compressor)) return this; return this.expression.drop_side_effect_free(compressor, first_in_statement); }); - def(AST_Sub, function(compressor, first_in_statement){ + def(AST_Sub, function(compressor, first_in_statement) { if (this.expression.may_throw_on_access(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); @@ -3994,7 +3977,7 @@ merge(Compressor.prototype, { if (!property) return expression; return make_sequence(this, [ expression, property ]); }); - def(AST_Sequence, function(compressor){ + def(AST_Sequence, function(compressor) { var last = this.tail_node(); var expr = last.drop_side_effect_free(compressor); if (expr === last) return this; @@ -4002,11 +3985,11 @@ merge(Compressor.prototype, { if (expr) expressions.push(expr); return make_sequence(this, expressions); }); - })(function(node, func){ + })(function(node, func) { node.DEFMETHOD("drop_side_effect_free", func); }); - OPT(AST_SimpleStatement, function(self, compressor){ + OPT(AST_SimpleStatement, function(self, compressor) { if (compressor.option("side_effects")) { var body = self.body; var node = body.drop_side_effect_free(compressor, true); @@ -4021,7 +4004,7 @@ merge(Compressor.prototype, { return self; }); - OPT(AST_While, function(self, compressor){ + OPT(AST_While, function(self, compressor) { return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self; }); @@ -4039,7 +4022,7 @@ merge(Compressor.prototype, { return found; } - OPT(AST_Do, function(self, compressor){ + OPT(AST_Do, function(self, compressor) { if (!compressor.option("loops")) return self; var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor); if (!(cond instanceof AST_Node)) { @@ -4142,7 +4125,7 @@ merge(Compressor.prototype, { } } - OPT(AST_For, function(self, compressor){ + OPT(AST_For, function(self, compressor) { if (!compressor.option("loops")) return self; if (compressor.option("side_effects") && self.init) { self.init = self.init.drop_side_effect_free(compressor); @@ -4191,7 +4174,7 @@ merge(Compressor.prototype, { return if_break_in_loop(self, compressor); }); - OPT(AST_If, function(self, compressor){ + OPT(AST_If, function(self, compressor) { if (is_empty(self.alternative)) self.alternative = null; if (!compressor.option("conditionals")) return self; @@ -4337,7 +4320,7 @@ merge(Compressor.prototype, { return self; }); - OPT(AST_Switch, function(self, compressor){ + OPT(AST_Switch, function(self, compressor) { if (!compressor.option("switches")) return self; var branch; var value = self.expression.evaluate(compressor); @@ -4443,7 +4426,7 @@ merge(Compressor.prototype, { } }); - OPT(AST_Try, function(self, compressor){ + OPT(AST_Try, function(self, compressor) { tighten_body(self.body, compressor); if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null; if (compressor.option("dead_code") && all(self.body, is_empty)) { @@ -4468,13 +4451,15 @@ merge(Compressor.prototype, { return self; }); - AST_Definitions.DEFMETHOD("remove_initializers", function(){ - this.definitions.forEach(function(def){ def.value = null }); + AST_Definitions.DEFMETHOD("remove_initializers", function() { + this.definitions.forEach(function(def) { + def.value = null; + }); }); - AST_Definitions.DEFMETHOD("to_assignments", function(compressor){ + AST_Definitions.DEFMETHOD("to_assignments", function(compressor) { var reduce_vars = compressor.option("reduce_vars"); - var assignments = this.definitions.reduce(function(a, def){ + 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, { @@ -4493,7 +4478,7 @@ merge(Compressor.prototype, { return make_sequence(this, assignments); }); - OPT(AST_Definitions, function(self, compressor){ + OPT(AST_Definitions, function(self, compressor) { if (self.definitions.length == 0) return make_node(AST_EmptyStatement, self); return self; @@ -4669,7 +4654,7 @@ merge(Compressor.prototype, { } else { first = make_node(AST_String, self, { value: "" }); } - return elements.reduce(function(prev, el){ + return elements.reduce(function(prev, el) { return make_node(AST_Binary, el, { operator : "+", left : prev, @@ -5048,7 +5033,7 @@ merge(Compressor.prototype, { return self; }); - OPT(AST_Sequence, function(self, compressor){ + OPT(AST_Sequence, function(self, compressor) { if (!compressor.option("side_effects")) return self; var expressions = []; filter_for_side_effects(); @@ -5086,7 +5071,7 @@ merge(Compressor.prototype, { } }); - AST_Unary.DEFMETHOD("lift_sequences", function(compressor){ + AST_Unary.DEFMETHOD("lift_sequences", function(compressor) { if (compressor.option("sequences") && this.expression instanceof AST_Sequence) { var x = this.expression.expressions.slice(); var e = this.clone(); @@ -5097,11 +5082,11 @@ merge(Compressor.prototype, { return this; }); - OPT(AST_UnaryPostfix, function(self, compressor){ + OPT(AST_UnaryPostfix, function(self, compressor) { return self.lift_sequences(compressor); }); - OPT(AST_UnaryPrefix, function(self, compressor){ + OPT(AST_UnaryPrefix, function(self, compressor) { var e = self.expression; if (self.operator == "delete" && !(e instanceof AST_SymbolRef @@ -5177,7 +5162,7 @@ merge(Compressor.prototype, { return self; }); - AST_Binary.DEFMETHOD("lift_sequences", function(compressor){ + AST_Binary.DEFMETHOD("lift_sequences", function(compressor) { if (compressor.option("sequences")) { if (this.left instanceof AST_Sequence) { var x = this.left.expressions.slice(); @@ -5218,7 +5203,7 @@ merge(Compressor.prototype, { || node instanceof AST_Object; } - OPT(AST_Binary, function(self, compressor){ + OPT(AST_Binary, function(self, compressor) { function reversible() { return self.left.is_constant() || self.right.is_constant() @@ -5666,7 +5651,7 @@ merge(Compressor.prototype, { return node; } - OPT(AST_SymbolRef, function(self, compressor){ + OPT(AST_SymbolRef, function(self, compressor) { var def = self.resolve_defines(compressor); if (def) { return def.optimize(compressor); @@ -5799,7 +5784,7 @@ merge(Compressor.prototype, { return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE; } - OPT(AST_Undefined, function(self, compressor){ + OPT(AST_Undefined, function(self, compressor) { if (compressor.option("unsafe_undefined")) { var undef = find_variable(compressor, "undefined"); if (undef) { @@ -5822,7 +5807,7 @@ merge(Compressor.prototype, { }); }); - OPT(AST_Infinity, function(self, compressor){ + OPT(AST_Infinity, function(self, compressor) { var lhs = is_lhs(compressor.self(), compressor.parent()); if (lhs && is_atomic(lhs, self)) return self; if (compressor.option("keep_infinity") @@ -5840,7 +5825,7 @@ merge(Compressor.prototype, { }); }); - OPT(AST_NaN, function(self, compressor){ + OPT(AST_NaN, function(self, compressor) { var lhs = is_lhs(compressor.self(), compressor.parent()); if (lhs && !is_atomic(lhs, self) || find_variable(compressor, "NaN")) { @@ -5880,7 +5865,7 @@ merge(Compressor.prototype, { var ASSIGN_OPS = makePredicate("+ - / * % >> << >>> | ^ &"); var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &"); - OPT(AST_Assign, function(self, compressor){ + OPT(AST_Assign, function(self, compressor) { var def; if (compressor.option("dead_code") && self.left instanceof AST_SymbolRef @@ -5940,7 +5925,7 @@ merge(Compressor.prototype, { } }); - OPT(AST_Conditional, function(self, compressor){ + OPT(AST_Conditional, function(self, compressor) { if (!compressor.option("conditionals")) return self; // This looks like lift_sequences(), should probably be under "sequences" if (self.condition instanceof AST_Sequence) { @@ -6160,7 +6145,7 @@ merge(Compressor.prototype, { } }); - OPT(AST_Boolean, function(self, compressor){ + OPT(AST_Boolean, function(self, compressor) { if (!compressor.option("booleans")) return self; if (compressor.in_boolean_context()) return make_node(AST_Number, self, { value: +self.value @@ -6186,7 +6171,7 @@ merge(Compressor.prototype, { }); }); - OPT(AST_Sub, function(self, compressor){ + OPT(AST_Sub, function(self, compressor) { var expr = self.expression; var prop = self.property; if (compressor.option("properties")) { @@ -6330,7 +6315,7 @@ merge(Compressor.prototype, { } }); - OPT(AST_Dot, function(self, compressor){ + OPT(AST_Dot, function(self, compressor) { if (self.property == "arguments" || self.property == "caller") { compressor.warn("Function.protoype.{prop} not supported [{file}:{line},{col}]", { prop: self.property, @@ -6392,19 +6377,27 @@ merge(Compressor.prototype, { return self; }); - OPT(AST_Return, function(self, compressor){ + OPT(AST_Return, function(self, compressor) { if (self.value && is_undefined(self.value, compressor)) { self.value = null; } return self; }); - OPT(AST_VarDef, function(self, compressor){ + OPT(AST_VarDef, function(self, compressor) { var defines = compressor.option("global_defs"); if (defines && HOP(defines, self.name.name)) { compressor.warn('global_defs ' + self.name.name + ' redefined [{file}:{line},{col}]', self.start); } return self; }); - -})(); +})(function(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; + }); +}); diff --git a/lib/mozilla-ast.js b/lib/mozilla-ast.js index 8e66c916..dace808a 100644 --- a/lib/mozilla-ast.js +++ b/lib/mozilla-ast.js @@ -43,11 +43,9 @@ "use strict"; -(function(){ - - var normalize_directives = function(body) { +(function() { + function normalize_directives(body) { var in_directive = true; - for (var i = 0; i < body.length; i++) { if (in_directive && body[i] instanceof AST_Statement && body[i].body instanceof AST_String) { body[i] = new AST_Directive({ @@ -59,9 +57,8 @@ in_directive = false; } } - return body; - }; + } var MOZ_TO_ME = { Program: function(M) { @@ -129,7 +126,7 @@ return new AST_Array({ start : my_start_token(M), end : my_end_token(M), - elements : M.elements.map(function(elem){ + elements : M.elements.map(function(elem) { return elem === null ? new AST_Hole() : from_moz(elem); }) }); @@ -138,7 +135,7 @@ return new AST_Object({ start : my_start_token(M), end : my_end_token(M), - properties : M.properties.map(function(prop){ + properties : M.properties.map(function(prop) { prop.type = "Property"; return from_moz(prop) }) @@ -480,7 +477,7 @@ endpos : range ? range[0] : moznode.start, raw : raw_token(moznode), }); - }; + } function my_end_token(moznode) { var loc = moznode.loc, end = loc && loc.end; @@ -495,7 +492,7 @@ endpos : range ? range[1] : moznode.end, raw : raw_token(moznode), }); - }; + } function map(moztype, mytype, propmap) { var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; @@ -507,7 +504,7 @@ me_to_moz += "return {\n" + "type: " + JSON.stringify(moztype); - if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){ + if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop) { var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop); if (!m) throw new Error("Can't understand property map: " + prop); var moz = m[1], how = m[2], my = m[3]; @@ -550,7 +547,7 @@ ); MOZ_TO_ME[moztype] = moz_to_me; def_to_moz(mytype, me_to_moz); - }; + } var FROM_MOZ_STACK = null; @@ -559,9 +556,9 @@ var ret = node != null ? MOZ_TO_ME[node.type](node) : null; FROM_MOZ_STACK.pop(); return ret; - }; + } - AST_Node.from_mozilla_ast = function(node){ + AST_Node.from_mozilla_ast = function(node) { var save_stack = FROM_MOZ_STACK; FROM_MOZ_STACK = []; var ast = from_moz(node); @@ -600,24 +597,24 @@ } } return moznode; - }; + } function def_to_moz(mytype, handler) { mytype.DEFMETHOD("to_mozilla_ast", function() { return set_moz_loc(this, handler(this)); }); - }; + } function to_moz(node) { return node != null ? node.to_mozilla_ast() : null; - }; + } function to_moz_block(node) { return { type: "BlockStatement", body: node.body.map(to_moz) }; - }; + } function to_moz_scope(type, node) { var body = node.body.map(to_moz); @@ -628,5 +625,5 @@ type: type, body: body }; - }; + } })(); diff --git a/lib/output.js b/lib/output.js index 6d7b0723..1dbc1f6e 100644 --- a/lib/output.js +++ b/lib/output.js @@ -137,7 +137,7 @@ function OutputStream(options) { 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){ + function(s, i) { switch (s) { case '"': ++dq; return '"'; case "'": ++sq; return "'"; @@ -408,7 +408,7 @@ function OutputStream(options) { var ret; print("{"); newline(); - with_indent(next_indent(), function(){ + with_indent(next_indent(), function() { ret = cont(); }); indent(); @@ -631,7 +631,7 @@ function OutputStream(options) { /* -----[ code generators ]----- */ -(function(){ +(function() { /* -----[ utils ]----- */ @@ -643,7 +643,7 @@ function OutputStream(options) { var active_scope = null; var use_asm = null; - AST_Node.DEFMETHOD("print", function(stream, force_parens){ + AST_Node.DEFMETHOD("print", function(stream, force_parens) { var self = this, generator = self._codegen; if (self instanceof AST_Scope) { active_scope = self; @@ -670,7 +670,7 @@ function OutputStream(options) { }); AST_Node.DEFMETHOD("_print", AST_Node.prototype.print); - AST_Node.DEFMETHOD("print_to_string", function(options){ + AST_Node.DEFMETHOD("print_to_string", function(options) { var s = OutputStream(options); this.print(s); return s.get(); @@ -680,7 +680,7 @@ function OutputStream(options) { function PARENS(nodetype, func) { if (Array.isArray(nodetype)) { - nodetype.forEach(function(nodetype){ + nodetype.forEach(function(nodetype) { PARENS(nodetype, func); }); } else { @@ -692,7 +692,7 @@ function OutputStream(options) { // a function expression needs parens around it when it's provably // the first token to appear in a statement. - PARENS(AST_Function, function(output){ + PARENS(AST_Function, function(output) { if (!output.has_parens() && first_in_statement(output)) { return true; } @@ -714,17 +714,17 @@ function OutputStream(options) { // same goes for an object literal, because otherwise it would be // interpreted as a block of code. - PARENS(AST_Object, function(output){ + PARENS(AST_Object, function(output) { return !output.has_parens() && first_in_statement(output); }); - PARENS(AST_Unary, function(output){ + PARENS(AST_Unary, function(output) { var p = output.parent(); return p instanceof AST_PropAccess && p.expression === this || p instanceof AST_Call && p.expression === this; }); - PARENS(AST_Sequence, function(output){ + PARENS(AST_Sequence, 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) @@ -738,7 +738,7 @@ function OutputStream(options) { ; }); - PARENS(AST_Binary, function(output){ + PARENS(AST_Binary, function(output) { var p = output.parent(); // (foo && bar)() if (p instanceof AST_Call && p.expression === this) @@ -761,7 +761,7 @@ function OutputStream(options) { } }); - PARENS(AST_PropAccess, function(output){ + PARENS(AST_PropAccess, function(output) { var p = output.parent(); if (p instanceof AST_New && p.expression === this) { // i.e. new (foo.bar().baz) @@ -782,7 +782,7 @@ function OutputStream(options) { } }); - PARENS(AST_Call, function(output){ + PARENS(AST_Call, function(output) { var p = output.parent(), p1; if (p instanceof AST_New && p.expression === this) return true; @@ -796,7 +796,7 @@ function OutputStream(options) { && p1.left === p; }); - PARENS(AST_New, function(output){ + PARENS(AST_New, function(output) { var p = output.parent(); if (!need_constructor_parens(this, output) && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]() @@ -804,7 +804,7 @@ function OutputStream(options) { return true; }); - PARENS(AST_Number, function(output){ + PARENS(AST_Number, function(output) { var p = output.parent(); if (p instanceof AST_PropAccess && p.expression === this) { var value = this.getValue(); @@ -814,7 +814,7 @@ function OutputStream(options) { } }); - PARENS([ AST_Assign, AST_Conditional ], function(output){ + PARENS([ AST_Assign, AST_Conditional ], function(output) { var p = output.parent(); // !(a = false) → true if (p instanceof AST_Unary) @@ -835,11 +835,11 @@ function OutputStream(options) { /* -----[ PRINTERS ]----- */ - DEFPRINT(AST_Directive, function(self, output){ + DEFPRINT(AST_Directive, function(self, output) { output.print_string(self.value, self.quote); output.semicolon(); }); - DEFPRINT(AST_Debugger, function(self, output){ + DEFPRINT(AST_Debugger, function(self, output) { output.print("debugger"); output.semicolon(); }); @@ -849,7 +849,7 @@ function OutputStream(options) { function display_body(body, is_toplevel, output, allow_directives) { var last = body.length - 1; in_directive = allow_directives; - body.forEach(function(stmt, i){ + body.forEach(function(stmt, i) { if (in_directive === true && !(stmt instanceof AST_Directive || stmt instanceof AST_EmptyStatement || (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) @@ -874,24 +874,24 @@ function OutputStream(options) { in_directive = false; } - AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){ + AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) { force_statement(this.body, output); }); - DEFPRINT(AST_Statement, function(self, output){ + DEFPRINT(AST_Statement, function(self, output) { self.body.print(output); output.semicolon(); }); - DEFPRINT(AST_Toplevel, function(self, output){ + DEFPRINT(AST_Toplevel, function(self, output) { display_body(self.body, true, output, true); output.print(""); }); - DEFPRINT(AST_LabeledStatement, function(self, output){ + DEFPRINT(AST_LabeledStatement, function(self, output) { self.label.print(output); output.colon(); self.body.print(output); }); - DEFPRINT(AST_SimpleStatement, function(self, output){ + DEFPRINT(AST_SimpleStatement, function(self, output) { self.body.print(output); output.semicolon(); }); @@ -909,37 +909,37 @@ function OutputStream(options) { }); } else print_braced_empty(self, output); } - DEFPRINT(AST_BlockStatement, function(self, output){ + DEFPRINT(AST_BlockStatement, function(self, output) { print_braced(self, output); }); - DEFPRINT(AST_EmptyStatement, function(self, output){ + DEFPRINT(AST_EmptyStatement, function(self, output) { output.semicolon(); }); - DEFPRINT(AST_Do, function(self, output){ + DEFPRINT(AST_Do, function(self, output) { output.print("do"); output.space(); make_block(self.body, output); output.space(); output.print("while"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.condition.print(output); }); output.semicolon(); }); - DEFPRINT(AST_While, function(self, output){ + DEFPRINT(AST_While, function(self, output) { output.print("while"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.condition.print(output); }); output.space(); self._do_print_body(output); }); - DEFPRINT(AST_For, function(self, output){ + DEFPRINT(AST_For, function(self, output) { output.print("for"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { if (self.init) { if (self.init instanceof AST_Definitions) { self.init.print(output); @@ -965,10 +965,10 @@ function OutputStream(options) { output.space(); self._do_print_body(output); }); - DEFPRINT(AST_ForIn, function(self, output){ + DEFPRINT(AST_ForIn, function(self, output) { output.print("for"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.init.print(output); output.space(); output.print("in"); @@ -978,10 +978,10 @@ function OutputStream(options) { output.space(); self._do_print_body(output); }); - DEFPRINT(AST_With, function(self, output){ + DEFPRINT(AST_With, function(self, output) { output.print("with"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.expression.print(output); }); output.space(); @@ -989,7 +989,7 @@ function OutputStream(options) { }); /* -----[ functions ]----- */ - AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){ + AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword) { var self = this; if (!nokeyword) { output.print("function"); @@ -998,8 +998,8 @@ function OutputStream(options) { output.space(); self.name.print(output); } - output.with_parens(function(){ - self.argnames.forEach(function(arg, i){ + output.with_parens(function() { + self.argnames.forEach(function(arg, i) { if (i) output.comma(); arg.print(output); }); @@ -1007,40 +1007,31 @@ function OutputStream(options) { output.space(); print_braced(self, output, true); }); - DEFPRINT(AST_Lambda, function(self, output){ + DEFPRINT(AST_Lambda, function(self, output) { self._do_print(output); }); - /* -----[ exits ]----- */ - AST_Exit.DEFMETHOD("_do_print", function(output, kind){ + /* -----[ jumps ]----- */ + function print_jump(output, kind, target) { output.print(kind); - if (this.value) { + if (target) { output.space(); - this.value.print(output); + target.print(output); } output.semicolon(); - }); - DEFPRINT(AST_Return, function(self, output){ - self._do_print(output, "return"); - }); - DEFPRINT(AST_Throw, function(self, output){ - self._do_print(output, "throw"); - }); + } - /* -----[ loop control ]----- */ - AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){ - output.print(kind); - if (this.label) { - output.space(); - this.label.print(output); - } - output.semicolon(); + DEFPRINT(AST_Return, function(self, output) { + print_jump(output, "return", self.value); + }); + DEFPRINT(AST_Throw, function(self, output) { + print_jump(output, "throw", self.value); }); - DEFPRINT(AST_Break, function(self, output){ - self._do_print(output, "break"); + DEFPRINT(AST_Break, function(self, output) { + print_jump(output, "break", self.label); }); - DEFPRINT(AST_Continue, function(self, output){ - self._do_print(output, "continue"); + DEFPRINT(AST_Continue, function(self, output) { + print_jump(output, "continue", self.label); }); /* -----[ if ]----- */ @@ -1072,10 +1063,10 @@ function OutputStream(options) { } force_statement(self.body, output); } - DEFPRINT(AST_If, function(self, output){ + DEFPRINT(AST_If, function(self, output) { output.print("if"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.condition.print(output); }); output.space(); @@ -1094,17 +1085,17 @@ function OutputStream(options) { }); /* -----[ switch ]----- */ - DEFPRINT(AST_Switch, function(self, output){ + DEFPRINT(AST_Switch, function(self, output) { output.print("switch"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.expression.print(output); }); output.space(); var last = self.body.length - 1; if (last < 0) print_braced_empty(self, output); - else output.with_block(function(){ - self.body.forEach(function(branch, i){ + else output.with_block(function() { + self.body.forEach(function(branch, i) { output.indent(true); branch.print(output); if (i < last && branch.body.length > 0) @@ -1112,19 +1103,19 @@ function OutputStream(options) { }); }); }); - AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){ + AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output) { output.newline(); - this.body.forEach(function(stmt){ + this.body.forEach(function(stmt) { output.indent(); stmt.print(output); output.newline(); }); }); - DEFPRINT(AST_Default, function(self, output){ + DEFPRINT(AST_Default, function(self, output) { output.print("default:"); self._do_print_body(output); }); - DEFPRINT(AST_Case, function(self, output){ + DEFPRINT(AST_Case, function(self, output) { output.print("case"); output.space(); self.expression.print(output); @@ -1133,7 +1124,7 @@ function OutputStream(options) { }); /* -----[ exceptions ]----- */ - DEFPRINT(AST_Try, function(self, output){ + DEFPRINT(AST_Try, function(self, output) { output.print("try"); output.space(); print_braced(self, output); @@ -1146,37 +1137,30 @@ function OutputStream(options) { self.bfinally.print(output); } }); - DEFPRINT(AST_Catch, function(self, output){ + DEFPRINT(AST_Catch, function(self, output) { output.print("catch"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.argname.print(output); }); output.space(); print_braced(self, output); }); - DEFPRINT(AST_Finally, function(self, output){ + DEFPRINT(AST_Finally, function(self, output) { output.print("finally"); output.space(); print_braced(self, output); }); - /* -----[ var/const ]----- */ - AST_Definitions.DEFMETHOD("_do_print", function(output, kind){ - output.print(kind); + DEFPRINT(AST_Var, function(self, output) { + output.print("var"); output.space(); - this.definitions.forEach(function(def, i){ + self.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(); - }); - DEFPRINT(AST_Var, function(self, output){ - self._do_print(output, "var"); + if (p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon(); }); function parenthesize_for_noin(node, output, noin) { @@ -1193,7 +1177,7 @@ function OutputStream(options) { node.print(output, parens); } - DEFPRINT(AST_VarDef, function(self, output){ + DEFPRINT(AST_VarDef, function(self, output) { self.name.print(output); if (self.value) { output.space(); @@ -1206,28 +1190,27 @@ function OutputStream(options) { }); /* -----[ other expressions ]----- */ - DEFPRINT(AST_Call, function(self, output){ + DEFPRINT(AST_Call, function(self, output) { self.expression.print(output); if (self instanceof AST_New && !need_constructor_parens(self, output)) return; if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) { output.add_mapping(self.start); } - output.with_parens(function(){ - self.args.forEach(function(expr, i){ + output.with_parens(function() { + self.args.forEach(function(expr, i) { if (i) output.comma(); expr.print(output); }); }); }); - DEFPRINT(AST_New, function(self, output){ + DEFPRINT(AST_New, function(self, output) { output.print("new"); output.space(); AST_Call.prototype._codegen(self, output); }); - - AST_Sequence.DEFMETHOD("_do_print", function(output){ - this.expressions.forEach(function(node, index) { + DEFPRINT(AST_Sequence, function(self, output) { + self.expressions.forEach(function(node, index) { if (index > 0) { output.comma(); if (output.should_break()) { @@ -1238,18 +1221,7 @@ function OutputStream(options) { node.print(output); }); }); - DEFPRINT(AST_Sequence, 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); - // } - }); - DEFPRINT(AST_Dot, function(self, output){ + DEFPRINT(AST_Dot, function(self, output) { var expr = self.expression; expr.print(output); var prop = self.property; @@ -1270,13 +1242,13 @@ function OutputStream(options) { output.print_name(prop); } }); - DEFPRINT(AST_Sub, function(self, output){ + DEFPRINT(AST_Sub, function(self, output) { self.expression.print(output); output.print("["); self.property.print(output); output.print("]"); }); - DEFPRINT(AST_UnaryPrefix, function(self, output){ + DEFPRINT(AST_UnaryPrefix, function(self, output) { var op = self.operator; output.print(op); if (/^[a-z]/i.test(op) @@ -1287,11 +1259,11 @@ function OutputStream(options) { } self.expression.print(output); }); - DEFPRINT(AST_UnaryPostfix, function(self, output){ + DEFPRINT(AST_UnaryPostfix, function(self, output) { self.expression.print(output); output.print(self.operator); }); - DEFPRINT(AST_Binary, function(self, output){ + DEFPRINT(AST_Binary, function(self, output) { var op = self.operator; self.left.print(output); if (op[0] == ">" /* ">>" ">>>" ">" ">=" */ @@ -1317,7 +1289,7 @@ function OutputStream(options) { } self.right.print(output); }); - DEFPRINT(AST_Conditional, function(self, output){ + DEFPRINT(AST_Conditional, function(self, output) { self.condition.print(output); output.space(); output.print("?"); @@ -1329,11 +1301,11 @@ function OutputStream(options) { }); /* -----[ literals ]----- */ - DEFPRINT(AST_Array, function(self, output){ - output.with_square(function(){ + DEFPRINT(AST_Array, function(self, output) { + output.with_square(function() { var a = self.elements, len = a.length; if (len > 0) output.space(); - a.forEach(function(exp, i){ + 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 @@ -1345,9 +1317,9 @@ function OutputStream(options) { if (len > 0) output.space(); }); }); - DEFPRINT(AST_Object, function(self, output){ - if (self.properties.length > 0) output.with_block(function(){ - self.properties.forEach(function(prop, i){ + DEFPRINT(AST_Object, function(self, output) { + if (self.properties.length > 0) output.with_block(function() { + self.properties.forEach(function(prop, i) { if (i) { output.print(","); output.newline(); @@ -1376,7 +1348,7 @@ function OutputStream(options) { } } - DEFPRINT(AST_ObjectKeyVal, function(self, output){ + DEFPRINT(AST_ObjectKeyVal, function(self, output) { print_property_name(self.key, self.quote, output); output.colon(); self.value.print(output); @@ -1387,27 +1359,27 @@ function OutputStream(options) { print_property_name(this.key.name, this.quote, output); this.value._do_print(output, true); }); - DEFPRINT(AST_ObjectSetter, function(self, output){ + DEFPRINT(AST_ObjectSetter, function(self, output) { self._print_getter_setter("set", output); }); - DEFPRINT(AST_ObjectGetter, function(self, output){ + DEFPRINT(AST_ObjectGetter, function(self, output) { self._print_getter_setter("get", output); }); - DEFPRINT(AST_Symbol, function(self, output){ + DEFPRINT(AST_Symbol, function(self, output) { var def = self.definition(); output.print_name(def ? def.mangled_name || def.name : self.name); }); DEFPRINT(AST_Hole, noop); - DEFPRINT(AST_This, function(self, output){ + DEFPRINT(AST_This, function(self, output) { output.print("this"); }); - DEFPRINT(AST_Constant, function(self, output){ + DEFPRINT(AST_Constant, function(self, output) { output.print(self.getValue()); }); - DEFPRINT(AST_String, function(self, output){ + DEFPRINT(AST_String, function(self, output) { output.print_string(self.getValue(), self.quote, in_directive); }); - DEFPRINT(AST_Number, function(self, output){ + DEFPRINT(AST_Number, function(self, output) { if (use_asm && self.start && self.start.raw != null) { output.print(self.start.raw); } else { @@ -1415,7 +1387,7 @@ function OutputStream(options) { } }); - DEFPRINT(AST_RegExp, function(self, output){ + DEFPRINT(AST_RegExp, function(self, output) { var regexp = self.getValue(); var str = regexp.toString(); if (regexp.raw_source) { @@ -1487,7 +1459,7 @@ function OutputStream(options) { output.print("{}"); else if (stmt instanceof AST_BlockStatement) stmt.print(output); - else output.with_block(function(){ + else output.with_block(function() { output.indent(); stmt.print(output); output.newline(); diff --git a/lib/parse.js b/lib/parse.js index 21c89676..29df370c 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -130,7 +130,7 @@ function is_letter(code) { return (code >= 97 && code <= 122) || (code >= 65 && code <= 90) || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code))); -}; +} function is_surrogate_pair_head(code) { if (typeof code == "string") @@ -146,11 +146,11 @@ function is_surrogate_pair_tail(code) { function is_digit(code) { return code >= 48 && code <= 57; -}; +} function is_alphanumeric_char(code) { return is_digit(code) || is_letter(code); -}; +} function is_unicode_digit(code) { return UNICODE.digit.test(String.fromCharCode(code)); @@ -158,19 +158,19 @@ function is_unicode_digit(code) { function is_unicode_combining_mark(ch) { return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch); -}; +} function is_unicode_connector_punctuation(ch) { return UNICODE.connector_punctuation.test(ch); -}; +} function is_identifier(name) { return !RESERVED_WORDS[name] && /^[a-z_$][a-z0-9_$]*$/i.test(name); -}; +} function is_identifier_start(code) { return code == 36 || code == 95 || is_letter(code); -}; +} function is_identifier_char(ch) { var code = ch.charCodeAt(0); @@ -182,11 +182,11 @@ function is_identifier_char(ch) { || is_unicode_connector_punctuation(ch) || is_unicode_digit(code) ; -}; +} -function is_identifier_string(str){ +function is_identifier_string(str) { return /^[a-z_$][a-z0-9_$]*$/i.test(str); -}; +} function parse_js_number(num) { if (RE_HEX_NUMBER.test(num)) { @@ -197,7 +197,7 @@ function parse_js_number(num) { var val = parseFloat(num); if (val == num) return val; } -}; +} function JS_Parse_Error(message, filename, line, col, pos) { this.message = message; @@ -205,7 +205,7 @@ function JS_Parse_Error(message, filename, line, col, pos) { this.line = line; this.col = col; this.pos = pos; -}; +} JS_Parse_Error.prototype = Object.create(Error.prototype); JS_Parse_Error.prototype.constructor = JS_Parse_Error; JS_Parse_Error.prototype.name = "SyntaxError"; @@ -213,11 +213,11 @@ configure_error_stack(JS_Parse_Error); function js_error(message, filename, line, col, pos) { throw new JS_Parse_Error(message, filename, line, col, pos); -}; +} function is_token(token, type, val) { return token.type == type && (val == null || token.value == val); -}; +} var EX_EOF = {}; @@ -239,7 +239,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { directive_stack : [] }; - function peek() { return S.text.charAt(S.pos); }; + function peek() { + return S.text.charAt(S.pos); + } function next(signal_eof, in_string) { var ch = S.text.charAt(S.pos++); @@ -258,15 +260,15 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { ++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; @@ -276,19 +278,19 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { 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) { @@ -321,27 +323,27 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { } 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 num = read_while(function(ch, i) { var code = ch.charCodeAt(0); switch (code) { case 120: case 88: // xX @@ -367,7 +369,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { } else { parse_error("Invalid syntax: " + num); } - }; + } function read_escaped_char(in_string) { var ch = next(true, in_string); @@ -390,7 +392,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { if (ch >= "0" && ch <= "7") return read_octal_escape_sequence(ch); return ch; - }; + } function read_octal_escape_sequence(ch) { // Read @@ -417,9 +419,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { num = (num << 4) | digit; } return num; - }; + } - var read_string = with_eof_error("Unterminated string constant", function(quote_char){ + var read_string = with_eof_error("Unterminated string constant", function(quote_char) { var quote = next(), ret = ""; for (;;) { var ch = next(true, true); @@ -447,9 +449,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { S.comments_before.push(token(type, ret, true)); S.regex_allowed = regex_allowed; return next_token; - }; + } - var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){ + var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function() { var regex_allowed = S.regex_allowed; var i = find("*/", true); var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n'); @@ -481,7 +483,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1); } return name; - }; + } var read_regexp = with_eof_error("Unterminated regular expression", function(source) { var prev_backslash = false, ch, in_class = false; @@ -523,9 +525,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { } else { return op; } - }; + } return token("operator", grow(prefix || next())); - }; + } function handle_slash() { next(); @@ -538,14 +540,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { return skip_multiline_comment(); } return S.regex_allowed ? read_regexp("") : read_operator("/"); - }; + } function handle_dot() { next(); return is_digit(peek().charCodeAt(0)) ? read_num(".") : token("punc", "."); - }; + } function read_word() { var word = read_name(); @@ -554,7 +556,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { : !KEYWORDS[word] ? token("name", word) : OPERATORS[word] ? token("operator", word) : token("keyword", word); - }; + } function with_eof_error(eof_error, cont) { return function(x) { @@ -565,7 +567,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { else throw ex; } }; - }; + } function next_token(force_regexp) { if (force_regexp != null) @@ -609,7 +611,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { break; } parse_error("Unexpected character '" + ch + "'"); - }; + } next_token.context = function(nc) { if (nc) S = nc; @@ -645,8 +647,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { } return next_token; - -}; +} /* -----[ Parser (constants) ]----- */ @@ -666,7 +667,7 @@ var UNARY_POSTFIX = makePredicate([ "--", "++" ]); var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); -var PRECEDENCE = (function(a, ret){ +var PRECEDENCE = function(a, ret) { for (var i = 0; i < a.length; ++i) { var b = a[i]; for (var j = 0; j < b.length; ++j) { @@ -674,28 +675,24 @@ var PRECEDENCE = (function(a, ret){ } } return ret; -})( - [ - ["||"], - ["&&"], - ["|"], - ["^"], - ["&"], - ["==", "===", "!=", "!=="], - ["<", ">", "<=", ">=", "in", "instanceof"], - [">>", "<<", ">>>"], - ["+", "-"], - ["*", "/", "%"] - ], - {} -); +}([ + ["||"], + ["&&"], + ["|"], + ["^"], + ["&"], + ["==", "===", "!=", "!=="], + ["<", ">", "<=", ">=", "in", "instanceof"], + [">>", "<<", ">>>"], + ["+", "-"], + ["*", "/", "%"] +], {}); var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]); /* -----[ Parser ]----- */ function parse($TEXT, options) { - options = defaults(options, { bare_returns : false, expression : false, @@ -724,9 +721,11 @@ function parse($TEXT, options) { function is(type, value) { return is_token(S.token, type, value); - }; + } - function peek() { return S.peeked || (S.peeked = S.input()); }; + function peek() { + return S.peeked || (S.peeked = S.input()); + } function next() { S.prev = S.token; @@ -740,11 +739,11 @@ function parse($TEXT, options) { 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(); @@ -753,26 +752,28 @@ function parse($TEXT, options) { 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 expect(punc) { + return expect_token("punc", punc); + } function has_newline_before(token) { return token.nlb || !all(token.comments_before, function(comment) { @@ -783,19 +784,19 @@ function parse($TEXT, options) { function can_insert_semicolon() { return !options.strict && (is("eof") || is("punc", "}") || has_newline_before(S.token)); - }; + } 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() { @@ -806,14 +807,14 @@ function parse($TEXT, options) { 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(strict_defun) { handle_regexp(); @@ -986,7 +987,7 @@ function parse($TEXT, options) { // check for `continue` that refers to this label. // those should be reported as syntax errors. // https://github.com/mishoo/UglifyJS2/issues/287 - label.references.forEach(function(ref){ + label.references.forEach(function(ref) { if (ref instanceof AST_Continue) { ref = ref.label.start; croak("Continue label `" + label.name + "` refers to non-IterationStatement.", @@ -995,11 +996,11 @@ function parse($TEXT, options) { }); } return new AST_LabeledStatement({ body: stat, label: label }); - }; + } function simple_statement(tmp) { return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) }); - }; + } function break_cont(type) { var label = null, ldef; @@ -1007,18 +1008,17 @@ function parse($TEXT, options) { label = as_symbol(AST_LabelRef, true); } if (label != null) { - ldef = find_if(function(l){ return l.name == label.name }, S.labels); - if (!ldef) - croak("Undefined label " + label.name); + ldef = find_if(function(l) { + return l.name == label.name; + }, S.labels); + if (!ldef) croak("Undefined label " + label.name); label.thedef = ldef; - } - else if (S.in_loop == 0) - croak(type.TYPE + " not inside a loop or switch"); + } else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch"); semicolon(); var stat = new type({ label: label }); if (ldef) ldef.references.push(stat); return stat; - }; + } function for_() { expect("("); @@ -1039,7 +1039,7 @@ function parse($TEXT, options) { } } return regular_for(init); - }; + } function regular_for(init) { expect(";"); @@ -1053,7 +1053,7 @@ function parse($TEXT, options) { step : step, body : in_loop(statement) }); - }; + } function for_in(init) { var obj = expression(true); @@ -1063,7 +1063,7 @@ function parse($TEXT, options) { object : obj, body : in_loop(statement) }); - }; + } var function_ = function(ctor) { var in_statement = ctor === AST_Defun; @@ -1113,7 +1113,7 @@ function parse($TEXT, options) { body : body, alternative : belse }); - }; + } function block_(strict_defun) { expect("{"); @@ -1124,7 +1124,7 @@ function parse($TEXT, options) { } next(); return a; - }; + } function switch_body_() { expect("{"); @@ -1159,7 +1159,7 @@ function parse($TEXT, options) { if (branch) branch.end = prev(); next(); return a; - }; + } function try_() { var body = block_(), bcatch = null, bfinally = null; @@ -1192,7 +1192,7 @@ function parse($TEXT, options) { bcatch : bcatch, bfinally : bfinally }); - }; + } function vardefs(no_in) { var a = []; @@ -1208,7 +1208,7 @@ function parse($TEXT, options) { next(); } return a; - }; + } var var_ = function(no_in) { return new AST_Var({ @@ -1274,7 +1274,7 @@ function parse($TEXT, options) { } next(); return ret; - }; + } var expr_atom = function(allow_calls) { if (is("operator", "new")) { @@ -1340,7 +1340,7 @@ function parse($TEXT, options) { } next(); return a; - }; + } var array_ = embed_tokens(function() { expect("["); @@ -1417,14 +1417,14 @@ function parse($TEXT, options) { default: unexpected(); } - }; + } function as_name() { var tmp = S.token; if (tmp.type != "name") unexpected(); next(); return tmp.value; - }; + } function _make_symbol(type) { var name = S.token.value; @@ -1433,7 +1433,7 @@ function parse($TEXT, options) { start : S.token, end : S.token }); - }; + } function strict_verify_symbol(sym) { if (sym.name == "arguments" || sym.name == "eval") @@ -1451,7 +1451,7 @@ function parse($TEXT, options) { } next(); return sym; - }; + } function mark_pure(call) { var start = call.start; @@ -1536,7 +1536,7 @@ function parse($TEXT, options) { break; } return new ctor({ operator: op, expression: expr }); - }; + } var expr_op = function(left, min_prec, no_in) { var op = is("operator") ? S.token.value : null; @@ -1558,7 +1558,7 @@ function parse($TEXT, options) { function expr_ops(no_in) { return expr_op(maybe_unary(true), 0, no_in); - }; + } var maybe_conditional = function(no_in) { var start = S.token; @@ -1580,7 +1580,7 @@ function parse($TEXT, options) { function is_assignable(expr) { return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef; - }; + } var maybe_assign = function(no_in) { var start = S.token; @@ -1622,13 +1622,13 @@ function parse($TEXT, options) { var ret = cont(); --S.in_loop; return ret; - }; + } if (options.expression) { return expression(true); } - return (function(){ + return function() { var start = S.token; var body = []; S.input.push_directives_stack(); @@ -1644,6 +1644,5 @@ function parse($TEXT, options) { toplevel = new AST_Toplevel({ start: start, body: body, end: end }); } return toplevel; - })(); - -}; + }(); +} diff --git a/lib/propmangle.js b/lib/propmangle.js index 1bca1ad4..7ad4804d 100644 --- a/lib/propmangle.js +++ b/lib/propmangle.js @@ -56,7 +56,7 @@ function find_builtins(reserved) { [ Object, Array, Function, Number, String, Boolean, Error, Math, Date, RegExp - ].forEach(function(ctor){ + ].forEach(function(ctor) { Object.getOwnPropertyNames(ctor).map(add); if (ctor.prototype) { Object.getOwnPropertyNames(ctor.prototype).map(add); @@ -136,7 +136,7 @@ function mangle_properties(ast, options) { var unmangleable = []; // step 1: find candidates to mangle - ast.walk(new TreeWalker(function(node){ + ast.walk(new TreeWalker(function(node) { if (node instanceof AST_ObjectKeyVal) { add(node.key); } @@ -157,7 +157,7 @@ function mangle_properties(ast, options) { })); // step 2: transform the tree, renaming properties - return ast.transform(new TreeTransformer(function(node){ + return ast.transform(new TreeTransformer(function(node) { if (node instanceof AST_ObjectKeyVal) { node.key = mangle(node.key); } @@ -234,7 +234,7 @@ function mangle_properties(ast, options) { } function mangleStrings(node) { - return node.transform(new TreeTransformer(function(node){ + return node.transform(new TreeTransformer(function(node) { if (node instanceof AST_Sequence) { var last = node.expressions.length - 1; node.expressions[last] = mangleStrings(node.expressions[last]); diff --git a/lib/scope.js b/lib/scope.js index fb14480f..d4b13d12 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -91,7 +91,7 @@ SymbolDef.prototype = { } }; -AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ +AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { options = defaults(options, { cache: null, ie8: false, @@ -101,7 +101,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ var self = this; var scope = self.parent_scope = null; var defun = null; - var tw = new TreeWalker(function(node, descend){ + var tw = new TreeWalker(function(node, descend) { if (node instanceof AST_Catch) { var save_scope = scope; scope = new AST_Scope(node); @@ -162,7 +162,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ // pass 2: find back references and eval self.globals = new Dictionary(); - var tw = new TreeWalker(function(node, descend){ + var tw = new TreeWalker(function(node, descend) { if (node instanceof AST_LoopControl && node.label) { node.label.thedef.references.push(node); return true; @@ -217,7 +217,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ } }); -AST_Toplevel.DEFMETHOD("def_global", function(node){ +AST_Toplevel.DEFMETHOD("def_global", function(node) { var globals = this.globals, name = node.name; if (globals.has(name)) { return globals.get(name); @@ -230,7 +230,7 @@ AST_Toplevel.DEFMETHOD("def_global", function(node){ } }); -AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){ +AST_Scope.DEFMETHOD("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 @@ -240,7 +240,7 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){ this.cname = -1; // the current index for mangling functions/variables }); -AST_Lambda.DEFMETHOD("init_scope_vars", function(){ +AST_Lambda.DEFMETHOD("init_scope_vars", function() { AST_Scope.prototype.init_scope_vars.apply(this, arguments); this.uses_arguments = false; this.def_variable(new AST_SymbolFunarg({ @@ -270,20 +270,20 @@ AST_Symbol.DEFMETHOD("reference", function(options) { this.mark_enclosed(options); }); -AST_Scope.DEFMETHOD("find_variable", function(name){ +AST_Scope.DEFMETHOD("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)); }); -AST_Scope.DEFMETHOD("def_function", function(symbol, init){ +AST_Scope.DEFMETHOD("def_function", function(symbol, init) { var def = this.def_variable(symbol, init); if (!def.init || def.init instanceof AST_Defun) def.init = init; this.functions.set(symbol.name, def); return def; }); -AST_Scope.DEFMETHOD("def_variable", function(symbol, init){ +AST_Scope.DEFMETHOD("def_variable", function(symbol, init) { var def = this.variables.get(symbol.name); if (def) { def.orig.push(symbol); @@ -390,7 +390,7 @@ function _default_mangler_options(options) { return options; } -AST_Toplevel.DEFMETHOD("mangle_names", function(options){ +AST_Toplevel.DEFMETHOD("mangle_names", function(options) { options = _default_mangler_options(options); // We only need to mangle declaration nodes. Special logic wired @@ -407,7 +407,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){ } var redefined = []; - var tw = new TreeWalker(function(node, descend){ + 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; @@ -520,7 +520,7 @@ AST_Sequence.DEFMETHOD("tail_node", function() { return this.expressions[this.expressions.length - 1]; }); -AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ +AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) { options = _default_mangler_options(options); base54.reset(); try { diff --git a/lib/sourcemap.js b/lib/sourcemap.js index dcb8e476..2feec45d 100644 --- a/lib/sourcemap.js +++ b/lib/sourcemap.js @@ -46,16 +46,15 @@ // a small wrapper around fitzgen's source-map library function SourceMap(options) { options = defaults(options, { - file : null, - root : null, - orig : null, - - orig_line_diff : 0, - dest_line_diff : 0, - }); + file: null, + root: null, + orig: null, + orig_line_diff: 0, + dest_line_diff: 0, + }, true); var generator = new MOZ_SourceMap.SourceMapGenerator({ - file : options.file, - sourceRoot : options.root + file: options.file, + sourceRoot: options.root }); var maps = options.orig && Object.create(null); if (maps) for (var source in options.orig) { @@ -68,30 +67,38 @@ function SourceMap(options) { } maps[source] = map; } - - function add(source, gen_line, gen_col, orig_line, orig_col, name) { - var map = maps && maps[source]; - if (map) { - var info = map.originalPositionFor({ - line: orig_line, - column: orig_col + return { + add: function(source, gen_line, gen_col, orig_line, orig_col, name) { + var map = maps && maps[source]; + if (map) { + var info = 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({ + name: name, + source: source, + generated: { + line: gen_line + options.dest_line_diff, + column: gen_col + }, + original: { + line: orig_line + options.orig_line_diff, + column: orig_col + } }); - if (info.source === null) return; - source = info.source; - orig_line = info.line; - orig_col = info.column; - name = info.name || name; + }, + get: function() { + return generator; + }, + toString: function() { + return JSON.stringify(generator.toJSON()); } - 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()); } }; -}; +} diff --git a/lib/transform.js b/lib/transform.js index 41b24c54..5897aa77 100644 --- a/lib/transform.js +++ b/lib/transform.js @@ -43,8 +43,6 @@ "use strict"; -// Tree transformer helpers. - function TreeTransformer(before, after) { TreeWalker.call(this); this.before = before; @@ -52,168 +50,136 @@ function TreeTransformer(before, after) { } TreeTransformer.prototype = new TreeWalker; -(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) { - x = this; - descend(x, tw); - if (tw.after) { - y = tw.after(x, in_list); - if (y !== undefined) x = y; - } - } - tw.pop(); - return x; - }); - }; - +(function(DEF) { function do_list(list, tw) { - return MAP(list, function(node){ + return MAP(list, function(node) { return node.transform(tw, true); }); - }; + } - _(AST_Node, noop); - - _(AST_LabeledStatement, function(self, tw){ + DEF(AST_Node, noop); + DEF(AST_LabeledStatement, function(self, tw) { self.label = self.label.transform(tw); self.body = self.body.transform(tw); }); - - _(AST_SimpleStatement, function(self, tw){ + DEF(AST_SimpleStatement, function(self, tw) { self.body = self.body.transform(tw); }); - - _(AST_Block, function(self, tw){ + DEF(AST_Block, function(self, tw) { self.body = do_list(self.body, tw); }); - - _(AST_Do, function(self, tw){ + DEF(AST_Do, function(self, tw) { self.body = self.body.transform(tw); self.condition = self.condition.transform(tw); }); - - _(AST_While, function(self, tw){ + DEF(AST_While, function(self, tw) { self.condition = self.condition.transform(tw); self.body = self.body.transform(tw); }); - - _(AST_For, function(self, tw){ + DEF(AST_For, function(self, tw) { if (self.init) self.init = self.init.transform(tw); if (self.condition) self.condition = self.condition.transform(tw); if (self.step) self.step = self.step.transform(tw); self.body = self.body.transform(tw); }); - - _(AST_ForIn, function(self, tw){ + DEF(AST_ForIn, function(self, tw) { self.init = self.init.transform(tw); self.object = self.object.transform(tw); self.body = self.body.transform(tw); }); - - _(AST_With, function(self, tw){ + DEF(AST_With, function(self, tw) { self.expression = self.expression.transform(tw); self.body = self.body.transform(tw); }); - - _(AST_Exit, function(self, tw){ + DEF(AST_Exit, function(self, tw) { if (self.value) self.value = self.value.transform(tw); }); - - _(AST_LoopControl, function(self, tw){ + DEF(AST_LoopControl, function(self, tw) { if (self.label) self.label = self.label.transform(tw); }); - - _(AST_If, function(self, tw){ + DEF(AST_If, function(self, tw) { self.condition = self.condition.transform(tw); self.body = self.body.transform(tw); if (self.alternative) self.alternative = self.alternative.transform(tw); }); - - _(AST_Switch, function(self, tw){ + DEF(AST_Switch, function(self, tw) { self.expression = self.expression.transform(tw); self.body = do_list(self.body, tw); }); - - _(AST_Case, function(self, tw){ + DEF(AST_Case, function(self, tw) { self.expression = self.expression.transform(tw); self.body = do_list(self.body, tw); }); - - _(AST_Try, function(self, tw){ + DEF(AST_Try, function(self, tw) { self.body = do_list(self.body, tw); if (self.bcatch) self.bcatch = self.bcatch.transform(tw); if (self.bfinally) self.bfinally = self.bfinally.transform(tw); }); - - _(AST_Catch, function(self, tw){ + DEF(AST_Catch, function(self, tw) { self.argname = self.argname.transform(tw); self.body = do_list(self.body, tw); }); - - _(AST_Definitions, function(self, tw){ + DEF(AST_Definitions, function(self, tw) { self.definitions = do_list(self.definitions, tw); }); - - _(AST_VarDef, function(self, tw){ + DEF(AST_VarDef, function(self, tw) { self.name = self.name.transform(tw); if (self.value) self.value = self.value.transform(tw); }); - - _(AST_Lambda, function(self, tw){ + DEF(AST_Lambda, function(self, tw) { if (self.name) self.name = self.name.transform(tw); self.argnames = do_list(self.argnames, tw); self.body = do_list(self.body, tw); }); - - _(AST_Call, function(self, tw){ + DEF(AST_Call, function(self, tw) { self.expression = self.expression.transform(tw); self.args = do_list(self.args, tw); }); - - _(AST_Sequence, function(self, tw){ + DEF(AST_Sequence, function(self, tw) { self.expressions = do_list(self.expressions, tw); }); - - _(AST_Dot, function(self, tw){ + DEF(AST_Dot, function(self, tw) { self.expression = self.expression.transform(tw); }); - - _(AST_Sub, function(self, tw){ + DEF(AST_Sub, function(self, tw) { self.expression = self.expression.transform(tw); self.property = self.property.transform(tw); }); - - _(AST_Unary, function(self, tw){ + DEF(AST_Unary, function(self, tw) { self.expression = self.expression.transform(tw); }); - - _(AST_Binary, function(self, tw){ + DEF(AST_Binary, function(self, tw) { self.left = self.left.transform(tw); self.right = self.right.transform(tw); }); - - _(AST_Conditional, function(self, tw){ + DEF(AST_Conditional, function(self, tw) { self.condition = self.condition.transform(tw); self.consequent = self.consequent.transform(tw); self.alternative = self.alternative.transform(tw); }); - - _(AST_Array, function(self, tw){ + DEF(AST_Array, function(self, tw) { self.elements = do_list(self.elements, tw); }); - - _(AST_Object, function(self, tw){ + DEF(AST_Object, function(self, tw) { self.properties = do_list(self.properties, tw); }); - - _(AST_ObjectProperty, function(self, tw){ + DEF(AST_ObjectProperty, function(self, tw) { self.value = self.value.transform(tw); }); - -})(); +})(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 (typeof x === "undefined") { + x = this; + descend(x, tw); + if (tw.after) { + y = tw.after(x, in_list); + if (typeof y !== "undefined") x = y; + } + } + tw.pop(); + return x; + }); +}); diff --git a/lib/utils.js b/lib/utils.js index d4d28348..ca4b2d4b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -115,7 +115,7 @@ function return_true() { return true; } function return_this() { return this; } function return_null() { return null; } -var MAP = (function(){ +var MAP = (function() { function MAP(a, f, backwards) { var ret = [], top = [], i; function doit() { @@ -138,7 +138,7 @@ var MAP = (function(){ } } return is_last; - }; + } if (Array.isArray(a)) { if (backwards) { for (i = a.length; --i >= 0;) if (doit()) break; @@ -152,14 +152,14 @@ var MAP = (function(){ for (i in a) if (HOP(a, i)) if (doit()) break; } return top.concat(ret); - }; + } MAP.at_top = function(val) { return new AtTop(val) }; MAP.splice = function(val) { return new Splice(val) }; MAP.last = function(val) { return new Last(val) }; var skip = MAP.skip = {}; - function AtTop(val) { this.v = val }; - function Splice(val) { this.v = val }; - function Last(val) { this.v = val }; + function AtTop(val) { this.v = val } + function Splice(val) { this.v = val } + function Last(val) { this.v = val } return MAP; })(); @@ -169,7 +169,7 @@ function push_uniq(array, el) { } function string_template(text, props) { - return text.replace(/\{(.+?)\}/g, function(str, p){ + return text.replace(/\{(.+?)\}/g, function(str, p) { return props && props[p]; }); } -- cgit v1.2.3