aboutsummaryrefslogtreecommitdiffstats
path: root/build
diff options
context:
space:
mode:
Diffstat (limited to 'build')
-rw-r--r--build/lib/parse-js.js40
-rw-r--r--build/lib/process.js96
-rw-r--r--build/uglify.js176
3 files changed, 222 insertions, 90 deletions
diff --git a/build/lib/parse-js.js b/build/lib/parse-js.js
index 9f90dfeec..8edecb733 100644
--- a/build/lib/parse-js.js
+++ b/build/lib/parse-js.js
@@ -751,14 +751,17 @@ function parse($TEXT, exigent_mode, embed_tokens) {
return str instanceof NodeWithToken ? str : new NodeWithToken(str, start, end);
};
- var statement = embed_tokens ? function() {
- var start = S.token;
- var ast = $statement.apply(this, arguments);
- ast[0] = add_tokens(ast[0], start, prev());
- return ast;
- } : $statement;
-
- function $statement() {
+ function maybe_embed_tokens(parser) {
+ if (embed_tokens) return function() {
+ var start = S.token;
+ var ast = parser.apply(this, arguments);
+ ast[0] = add_tokens(ast[0], start, prev());
+ return ast;
+ };
+ else return parser;
+ };
+
+ var statement = maybe_embed_tokens(function() {
if (is("operator", "/")) {
S.peeked = null;
S.token = S.input(true); // force regexp
@@ -852,7 +855,7 @@ function parse($TEXT, exigent_mode, embed_tokens) {
unexpected();
}
}
- };
+ });
function labeled_statement(label) {
S.labels.push(label);
@@ -910,14 +913,7 @@ function parse($TEXT, exigent_mode, embed_tokens) {
return as("for-in", init, lhs, obj, in_loop(statement));
};
- var function_ = embed_tokens ? function() {
- var start = prev();
- var ast = $function_.apply(this, arguments);
- ast[0] = add_tokens(ast[0], start, prev());
- return ast;
- } : $function_;
-
- function $function_(in_statement) {
+ var function_ = maybe_embed_tokens(function(in_statement) {
var name = is("name") ? prog1(S.token.value, next) : null;
if (in_statement && !name)
unexpected();
@@ -945,7 +941,7 @@ function parse($TEXT, exigent_mode, embed_tokens) {
S.in_loop = loop;
return a;
})());
- };
+ });
function if_() {
var cond = parenthesised(), body = statement(), belse;
@@ -1053,7 +1049,7 @@ function parse($TEXT, exigent_mode, embed_tokens) {
return subscripts(as("new", newexp, args), true);
};
- function expr_atom(allow_calls) {
+ var expr_atom = maybe_embed_tokens(function(allow_calls) {
if (is("operator", "new")) {
next();
return new_();
@@ -1088,7 +1084,7 @@ function parse($TEXT, exigent_mode, embed_tokens) {
return subscripts(prog1(atom, next), allow_calls);
}
unexpected();
- };
+ });
function expr_list(closing, allow_trailing_comma, allow_empty) {
var first = true, a = [];
@@ -1228,7 +1224,7 @@ function parse($TEXT, exigent_mode, embed_tokens) {
return left;
};
- function expression(commas, no_in) {
+ var expression = maybe_embed_tokens(function(commas, no_in) {
if (arguments.length == 0)
commas = true;
var expr = maybe_assign(no_in);
@@ -1237,7 +1233,7 @@ function parse($TEXT, exigent_mode, embed_tokens) {
return as("seq", expr, expression(true, no_in));
}
return expr;
- };
+ });
function in_loop(cont) {
try {
diff --git a/build/lib/process.js b/build/lib/process.js
index 09cbc2ad8..3878c8d62 100644
--- a/build/lib/process.js
+++ b/build/lib/process.js
@@ -75,6 +75,12 @@ function ast_walker(ast) {
return a;
}) ];
};
+ function _block(statements) {
+ var out = [ this[0] ];
+ if (statements != null)
+ out.push(MAP(statements, walk));
+ return out;
+ };
var walkers = {
"string": function(str) {
return [ this[0], str ];
@@ -88,12 +94,8 @@ function ast_walker(ast) {
"toplevel": function(statements) {
return [ this[0], MAP(statements, walk) ];
},
- "block": function(statements) {
- var out = [ this[0] ];
- if (statements != null)
- out.push(MAP(statements, walk));
- return out;
- },
+ "block": _block,
+ "splice": _block,
"var": _vardefs,
"const": _vardefs,
"try": function(t, c, f) {
@@ -377,7 +379,9 @@ function ast_add_scope(ast) {
};
function _lambda(name, args, body) {
- return [ this[0], this[0] == "defun" ? define(name) : name, args, with_new_scope(function(){
+ var is_defun = this[0] == "defun";
+ return [ this[0], is_defun ? define(name) : name, args, with_new_scope(function(){
+ if (!is_defun) define(name);
MAP(args, define);
return MAP(body, walk);
})];
@@ -463,9 +467,22 @@ 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) {
- if (name) name = get_mangled(name);
+ var is_defun = this[0] == "defun";
+ if (is_defun && name) name = get_mangled(name);
body = with_scope(body.scope, function(){
+ if (!is_defun && name) name = get_mangled(name);
args = MAP(args, function(name){ return get_mangled(name) });
return MAP(body, walk);
});
@@ -507,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],
@@ -583,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) {
@@ -676,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;
@@ -751,9 +787,14 @@ function ast_squeeze(ast, options) {
};
function _lambda(name, args, body) {
- return [ this[0], name, args, with_scope(body.scope, function(){
- return tighten(MAP(body, walk), "lambda");
- }) ];
+ var is_defun = this[0] == "defun";
+ body = with_scope(body.scope, function(){
+ var ret = tighten(MAP(body, walk), "lambda");
+ if (!is_defun && name && !HOP(scope.refs, name))
+ name = null;
+ return ret;
+ });
+ return [ this[0], name, args, body ];
};
// we get here for blocks that have been already transformed.
@@ -959,13 +1000,7 @@ function ast_squeeze(ast, options) {
return [ branch[0] ? walk(branch[0]) : null, block ];
}) ];
},
- "function": function() {
- var ret = _lambda.apply(this, arguments);
- if (ret[1] && !HOP(scope.refs, ret[1])) {
- ret[1] = null;
- }
- return ret;
- },
+ "function": _lambda,
"defun": _lambda,
"block": function(body) {
if (body) return rmblock([ "block", tighten(MAP(body, walk)) ]);
@@ -1067,6 +1102,8 @@ function to_ascii(str) {
});
};
+var SPLICE_NEEDS_BRACKETS = jsp.array_to_hash([ "if", "while", "do", "for", "for-in", "with" ]);
+
function gen_code(ast, options) {
options = defaults(options, {
indent_start : 0,
@@ -1197,6 +1234,19 @@ function gen_code(ast, options) {
return make_block_statements(statements)
.join(newline + newline);
},
+ "splice": function(statements) {
+ var parent = $stack[$stack.length - 2][0];
+ if (HOP(SPLICE_NEEDS_BRACKETS, parent)) {
+ // we need block brackets in this case
+ return make_block.apply(this, arguments);
+ } else {
+ return MAP(make_block_statements(statements, true),
+ function(line, i) {
+ // the first line is already indented
+ return i > 0 ? indent(line) : line;
+ }).join(newline);
+ }
+ },
"block": make_block,
"var": function(defs) {
return "var " + add_commas(MAP(defs, make_1vardef)) + ";";
@@ -1437,7 +1487,7 @@ function gen_code(ast, options) {
return add_spaces([ out, make_block(body) ]);
};
- function make_block_statements(statements) {
+ function make_block_statements(statements, noindent) {
for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) {
var stat = statements[i];
var code = make(stat);
@@ -1455,7 +1505,7 @@ function gen_code(ast, options) {
a.push(code);
}
}
- return MAP(a, indent);
+ return noindent ? a : MAP(a, indent);
};
function make_switch_block(body) {
diff --git a/build/uglify.js b/build/uglify.js
index 943ddd806..aad18e8ca 100644
--- a/build/uglify.js
+++ b/build/uglify.js
@@ -1,14 +1,10 @@
#! /usr/bin/env node
-// -*- js2 -*-
+// -*- js -*-
global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
-var fs = require("fs"),
- jsp = require("./lib/parse-js"),
- pro = require("./lib/process");
-
-pro.set_logger(function(msg){
- sys.debug(msg);
-});
+var fs = require("fs");
+var jsp = require("./lib/parse-js"),
+ pro = require("./lib/process");
var options = {
ast: false,
@@ -17,13 +13,16 @@ var options = {
squeeze: true,
make_seqs: true,
dead_code: true,
- beautify: false,
verbose: false,
show_copyright: true,
out_same_file: false,
- extra: false,
- unsafe: false, // XXX: extra & unsafe? but maybe we don't want both, so....
- beautify_options: {
+ max_line_length: 32 * 1024,
+ unsafe: false,
+ reserved_names: null,
+ defines: { },
+ codegen_options: {
+ ascii_only: false,
+ beautify: false,
indent_level: 4,
indent_start: 0,
quote_keys: false,
@@ -40,15 +39,15 @@ out: while (args.length > 0) {
switch (v) {
case "-b":
case "--beautify":
- options.beautify = true;
+ options.codegen_options.beautify = true;
break;
case "-i":
case "--indent":
- options.beautify_options.indent_level = args.shift();
+ options.codegen_options.indent_level = args.shift();
break;
case "-q":
case "--quote-keys":
- options.beautify_options.quote_keys = true;
+ options.codegen_options.quote_keys = true;
break;
case "-mt":
case "--mangle-toplevel":
@@ -86,23 +85,109 @@ out: while (args.length > 0) {
case "--ast":
options.ast = true;
break;
- case "--extra":
- options.extra = true;
- break;
case "--unsafe":
options.unsafe = true;
break;
+ case "--max-line-len":
+ options.max_line_length = parseInt(args.shift(), 10);
+ break;
+ 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;
default:
filename = v;
break out;
}
}
+if (options.verbose) {
+ pro.set_logger(function(msg){
+ sys.debug(msg);
+ });
+}
+
+jsp.set_logger(function(msg){
+ sys.debug(msg);
+});
+
if (filename) {
fs.readFile(filename, "utf8", function(err, text){
- if (err) {
- throw err;
- }
+ if (err) throw err;
output(squeeze_it(text));
});
} else {
@@ -131,7 +216,9 @@ function output(text) {
});
}
out.write(text);
- out.end();
+ if (options.output !== true) {
+ out.end();
+ }
};
// --------- main ends here.
@@ -152,36 +239,35 @@ function show_copyright(comments) {
function squeeze_it(code) {
var result = "";
if (options.show_copyright) {
- var initial_comments = [];
- // keep first comment
- var tok = jsp.tokenizer(code, false), c;
+ var tok = jsp.tokenizer(code), c;
c = tok();
- var prev = null;
- while (/^comment/.test(c.type) && (!prev || prev == c.type)) {
- initial_comments.push(c);
- prev = c.type;
- c = tok();
- }
- result += show_copyright(initial_comments);
+ result += show_copyright(c.comments_before);
}
try {
var ast = time_it("parse", function(){ return jsp.parse(code); });
- if (options.mangle)
- ast = time_it("mangle", function(){ return pro.ast_mangle(ast, options.mangle_toplevel); });
- if (options.squeeze)
- ast = time_it("squeeze", function(){
- ast = pro.ast_squeeze(ast, {
- make_seqs : options.make_seqs,
- dead_code : options.dead_code,
- extra : options.extra
- });
- if (options.unsafe)
- ast = pro.ast_squeeze_more(ast);
- return ast;
+ if (options.mangle) ast = time_it("mangle", function(){
+ return pro.ast_mangle(ast, {
+ toplevel: options.mangle_toplevel,
+ defines: options.defines,
+ except: options.reserved_names
+ });
+ });
+ if (options.squeeze) ast = time_it("squeeze", function(){
+ ast = pro.ast_squeeze(ast, {
+ make_seqs : options.make_seqs,
+ dead_code : options.dead_code,
+ keep_comps : !options.unsafe
});
+ if (options.unsafe)
+ ast = pro.ast_squeeze_more(ast);
+ return ast;
+ });
if (options.ast)
return sys.inspect(ast, null, null);
- result += time_it("generate", function(){ return pro.gen_code(ast, options.beautify && options.beautify_options) });
+ result += time_it("generate", function(){ return pro.gen_code(ast, options.codegen_options) });
+ if (!options.codegen_options.beautify && options.max_line_length) {
+ result = time_it("split", function(){ return pro.split_lines(result, options.max_line_length) });
+ }
return result;
} catch(ex) {
sys.debug(ex.stack);