Permalink
Browse files

Merged pull request #137 from tav/master.

Fix for issue 125: constant folding VARIABLES
  • Loading branch information...
2 parents 8f4fe4c + df17381 commit 70cb9ee3f104d3e85765414f648d6e829adca662 @mishoo mishoo committed Apr 29, 2011
Showing with 107 additions and 3 deletions.
  1. +74 −0 bin/uglifyjs
  2. +33 −3 lib/process.js
View
@@ -20,6 +20,7 @@ var options = {
max_line_length: 32 * 1024,
unsafe: false,
reserved_names: null,
+ defines: { },
codegen_options: {
ascii_only: false,
beautify: false,
@@ -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;
@@ -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
});
});
View
@@ -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);
@@ -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],
@@ -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) {
@@ -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;

0 comments on commit 70cb9ee

Please sign in to comment.