Skip to content

Commit

Permalink
Merged pull request mishoo#137 from tav/master.
Browse files Browse the repository at this point in the history
Fix for issue 125: constant folding VARIABLES
  • Loading branch information
mishoo committed Apr 29, 2011
2 parents 8f4fe4c + df17381 commit 70cb9ee
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 3 deletions.
74 changes: 74 additions & 0 deletions bin/uglifyjs
Expand Up @@ -20,6 +20,7 @@ var options = {
max_line_length: 32 * 1024,
unsafe: false,
reserved_names: null,
defines: { },
codegen_options: {
ascii_only: false,
beautify: false,
Expand Down Expand Up @@ -94,6 +95,78 @@ out: while (args.length > 0) {
case "--reserved-names":
options.reserved_names = args.shift().split(",");
break;
case "-d":
case "--define":
var defarg = args.shift();
try {
var defsym = function(sym) {
// KEYWORDS_ATOM doesn't include NaN or Infinity - should we check
// for them too ?? We don't check reserved words and the like as the
// define values are only substituted AFTER parsing
if (jsp.KEYWORDS_ATOM.hasOwnProperty(sym)) {
throw "Don't define values for inbuilt constant '"+sym+"'";
}
return sym;
},
defval = function(v) {
if (v.match(/^"(.*)"$/) || v.match(/^'(.*)'$/)) {
return [ "string", RegExp.$1 ];
}
else if (!isNaN(parseFloat(v))) {
return [ "num", parseFloat(v) ];
}
else if (v.match(/^[a-z\$_][a-z\$_0-9]*$/i)) {
return [ "name", v ];
}
else if (!v.match(/"/)) {
return [ "string", v ];
}
else if (!v.match(/'/)) {
return [ "string", v ];
}
throw "Can't understand the specified value: "+v;
};
if (defarg.match(/^([a-z_\$][a-z_\$0-9]*)(=(.*))?$/i)) {
var sym = defsym(RegExp.$1),
val = RegExp.$2 ? defval(RegExp.$2.substr(1)) : [ 'name', 'true' ];
options.defines[sym] = val;
}
else {
throw "The --define option expects SYMBOL[=value]";
}
} catch(ex) {
sys.print("ERROR: In option --define "+defarg+"\n"+ex+"\n");
process.exit(1);
}
break;
case "--define-from-module":
var defmodarg = args.shift(),
defmodule = require(defmodarg),
sym,
val;
for (sym in defmodule) {
if (defmodule.hasOwnProperty(sym)) {
options.defines[sym] = function(val) {
if (typeof val == "string")
return [ "string", val ];
if (typeof val == "number")
return [ "num", val ];
if (val === true)
return [ 'name', 'true' ];
if (val === false)
return [ 'name', 'false' ];
if (val === null)
return [ 'name', 'null' ];
if (val === undefined)
return [ 'name', 'undefined' ];
sys.print("ERROR: In option --define-from-module "+defmodarg+"\n");
sys.print("ERROR: Unknown object type for: "+sym+"="+val+"\n");
process.exit(1);
return null;
}(defmodule[sym]);
}
}
break;
case "--ascii":
options.codegen_options.ascii_only = true;
break;
Expand Down Expand Up @@ -176,6 +249,7 @@ function squeeze_it(code) {
if (options.mangle) ast = time_it("mangle", function(){
return pro.ast_mangle(ast, {
toplevel: options.mangle_toplevel,
defines: options.defines,
except: options.reserved_names
});
});
Expand Down
36 changes: 33 additions & 3 deletions lib/process.js
Expand Up @@ -467,6 +467,17 @@ function ast_mangle(ast, options) {
return scope.get_mangled(name, newMangle);
};

function get_define(name) {
// we always lookup a defined symbol for the current scope FIRST, so declared
// vars trump a DEFINE symbol, but if no such var is found, then match a DEFINE value
if (!scope.has(name)) {
if (HOP(options.defines, name)) {
return options.defines[name];
}
}
return null;
};

function _lambda(name, args, body) {
var is_defun = this[0] == "defun";
if (is_defun && name) name = get_mangled(name);
Expand Down Expand Up @@ -513,7 +524,7 @@ function ast_mangle(ast, options) {
"var": _vardefs,
"const": _vardefs,
"name": function(name) {
return [ this[0], get_mangled(name) ];
return get_define(name) || [ this[0], get_mangled(name) ];
},
"try": function(t, c, f) {
return [ this[0],
Expand Down Expand Up @@ -589,11 +600,18 @@ function boolean_expr(expr) {
};

function make_conditional(c, t, e) {
var make_real_conditional = function() {
if (c[0] == "unary-prefix" && c[1] == "!") {
return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
} else {
return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ];
return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ];
}
};
// shortcut the conditional if the expression has a constant value
return when_constant(c, function(ast, val){
warn_unreachable(val ? e : t);
return (val ? t : e);
}, make_real_conditional);
};

function empty(b) {
Expand Down Expand Up @@ -682,6 +700,18 @@ var when_constant = (function(){
|| (boolean_expr(expr[2]) && boolean_expr(expr[3])))) {
expr[1] = expr[1].substr(0, 2);
}
else if (no && expr[0] == "binary"
&& (expr[1] == "||" || expr[1] == "&&")) {
// the whole expression is not constant but the lval may be...
try {
var lval = evaluate(expr[2]);
expr = ((expr[1] == "&&" && (lval ? expr[3] : lval)) ||
(expr[1] == "||" && (lval ? lval : expr[3])) ||
expr);
} catch(ex2) {
// IGNORE... lval is not constant
}
}
return no ? no.call(expr, expr) : null;
}
else throw ex;
Expand Down

0 comments on commit 70cb9ee

Please sign in to comment.