aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/eliminator/node_modules/uglify-js/lib/process.js2560
1 files changed, 742 insertions, 1818 deletions
diff --git a/tools/eliminator/node_modules/uglify-js/lib/process.js b/tools/eliminator/node_modules/uglify-js/lib/process.js
index c3abb6f8..70a4b0a4 100644
--- a/tools/eliminator/node_modules/uglify-js/lib/process.js
+++ b/tools/eliminator/node_modules/uglify-js/lib/process.js
@@ -1,6 +1,6 @@
/***********************************************************************
- A JavaScript tokenizer / parser / beautifier / compressor.
+ A JavaScript tokenizer / parser / beautifier.
This version is suitable for Node.js. With minimal changes (the
exports stuff) it should work on any JS platform.
@@ -10,12 +10,6 @@
Exported functions:
- - ast_mangle(ast, options) -- mangles the variable/function names
- in the AST. Returns an AST.
-
- - ast_squeeze(ast) -- employs various optimizations to make the
- final generated code even smaller. Returns an AST.
-
- gen_code(ast, options) -- generates JS code from the AST. Pass
true (or an object, see the code for some options) as second
argument to get "pretty" (indented) code.
@@ -67,1835 +61,773 @@ var jsp = require("./parse-js"),
/* -----[ helper for AST traversal ]----- */
function ast_walker() {
- function _vardefs(defs) {
- return [ this[0], MAP(defs, function(def){
- var a = [ def[0] ];
- if (def.length > 1)
- a[1] = walk(def[1]);
- 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 ];
- },
- "num": function(num) {
- return [ this[0], num ];
- },
- "name": function(name) {
- return [ this[0], name ];
- },
- "toplevel": function(statements) {
- return [ this[0], MAP(statements, walk) ];
- },
- "block": _block,
- "splice": _block,
- "var": _vardefs,
- "const": _vardefs,
- "try": function(t, c, f) {
- return [
- this[0],
- MAP(t, walk),
- c != null ? [ c[0], MAP(c[1], walk) ] : null,
- f != null ? MAP(f, walk) : null
- ];
- },
- "throw": function(expr) {
- return [ this[0], walk(expr) ];
- },
- "new": function(ctor, args) {
- return [ this[0], walk(ctor), MAP(args, walk) ];
- },
- "switch": function(expr, body) {
- return [ this[0], walk(expr), MAP(body, function(branch){
- return [ branch[0] ? walk(branch[0]) : null,
- MAP(branch[1], walk) ];
- }) ];
- },
- "break": function(label) {
- return [ this[0], label ];
- },
- "continue": function(label) {
- return [ this[0], label ];
- },
- "conditional": function(cond, t, e) {
- return [ this[0], walk(cond), walk(t), walk(e) ];
- },
- "assign": function(op, lvalue, rvalue) {
- return [ this[0], op, walk(lvalue), walk(rvalue) ];
- },
- "dot": function(expr) {
- return [ this[0], walk(expr) ].concat(slice(arguments, 1));
- },
- "call": function(expr, args) {
- return [ this[0], walk(expr), MAP(args, walk) ];
- },
- "function": function(name, args, body) {
- return [ this[0], name, args.slice(), MAP(body, walk) ];
- },
- "defun": function(name, args, body) {
- return [ this[0], name, args.slice(), MAP(body, walk) ];
- },
- "if": function(conditional, t, e) {
- return [ this[0], walk(conditional), walk(t), walk(e) ];
- },
- "for": function(init, cond, step, block) {
- return [ this[0], walk(init), walk(cond), walk(step), walk(block) ];
- },
- "for-in": function(vvar, key, hash, block) {
- return [ this[0], walk(vvar), walk(key), walk(hash), walk(block) ];
- },
- "while": function(cond, block) {
- return [ this[0], walk(cond), walk(block) ];
- },
- "do": function(cond, block) {
- return [ this[0], walk(cond), walk(block) ];
- },
- "return": function(expr) {
- return [ this[0], walk(expr) ];
- },
- "binary": function(op, left, right) {
- return [ this[0], op, walk(left), walk(right) ];
- },
- "unary-prefix": function(op, expr) {
- return [ this[0], op, walk(expr) ];
- },
- "unary-postfix": function(op, expr) {
- return [ this[0], op, walk(expr) ];
- },
- "sub": function(expr, subscript) {
- return [ this[0], walk(expr), walk(subscript) ];
- },
- "object": function(props) {
- return [ this[0], MAP(props, function(p){
- return p.length == 2
- ? [ p[0], walk(p[1]) ]
- : [ p[0], walk(p[1]), p[2] ]; // get/set-ter
- }) ];
- },
- "regexp": function(rx, mods) {
- return [ this[0], rx, mods ];
- },
- "array": function(elements) {
- return [ this[0], MAP(elements, walk) ];
- },
- "stat": function(stat) {
- return [ this[0], walk(stat) ];
- },
- "seq": function() {
- return [ this[0] ].concat(MAP(slice(arguments), walk));
- },
- "label": function(name, block) {
- return [ this[0], name, walk(block) ];
- },
- "with": function(expr, block) {
- return [ this[0], walk(expr), walk(block) ];
- },
- "atom": function(name) {
- return [ this[0], name ];
- }
- };
-
- var user = {};
- var stack = [];
- function walk(ast) {
- if (ast == null)
- return null;
- try {
- stack.push(ast);
- var type = ast[0];
- var gen = user[type];
- if (gen) {
- var ret = gen.apply(ast, ast.slice(1));
- if (ret != null)
- return ret;
- }
- gen = walkers[type];
- return gen.apply(ast, ast.slice(1));
- } finally {
- stack.pop();
- }
- };
-
- function with_walkers(walkers, cont){
- var save = {}, i;
- for (i in walkers) if (HOP(walkers, i)) {
- save[i] = user[i];
- user[i] = walkers[i];
- }
- var ret = cont();
- for (i in save) if (HOP(save, i)) {
- if (!save[i]) delete user[i];
- else user[i] = save[i];
- }
- return ret;
- };
-
- return {
- walk: walk,
- with_walkers: with_walkers,
- parent: function() {
- return stack[stack.length - 2]; // last one is current node
- },
- stack: function() {
- return stack;
- }
- };
+ function _vardefs(defs) {
+ return [ this[0], MAP(defs, function(def){
+ var a = [ def[0] ];
+ if (def.length > 1)
+ a[1] = walk(def[1]);
+ 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 ];
+ },
+ "num": function(num) {
+ return [ this[0], num ];
+ },
+ "name": function(name) {
+ return [ this[0], name ];
+ },
+ "toplevel": function(statements) {
+ return [ this[0], MAP(statements, walk) ];
+ },
+ "block": _block,
+ "splice": _block,
+ "var": _vardefs,
+ "const": _vardefs,
+ "try": function(t, c, f) {
+ return [
+ this[0],
+ MAP(t, walk),
+ c != null ? [ c[0], MAP(c[1], walk) ] : null,
+ f != null ? MAP(f, walk) : null
+ ];
+ },
+ "throw": function(expr) {
+ return [ this[0], walk(expr) ];
+ },
+ "new": function(ctor, args) {
+ return [ this[0], walk(ctor), MAP(args, walk) ];
+ },
+ "switch": function(expr, body) {
+ return [ this[0], walk(expr), MAP(body, function(branch){
+ return [ branch[0] ? walk(branch[0]) : null,
+ MAP(branch[1], walk) ];
+ }) ];
+ },
+ "break": function(label) {
+ return [ this[0], label ];
+ },
+ "continue": function(label) {
+ return [ this[0], label ];
+ },
+ "conditional": function(cond, t, e) {
+ return [ this[0], walk(cond), walk(t), walk(e) ];
+ },
+ "assign": function(op, lvalue, rvalue) {
+ return [ this[0], op, walk(lvalue), walk(rvalue) ];
+ },
+ "dot": function(expr) {
+ return [ this[0], walk(expr) ].concat(slice(arguments, 1));
+ },
+ "call": function(expr, args) {
+ return [ this[0], walk(expr), MAP(args, walk) ];
+ },
+ "function": function(name, args, body) {
+ return [ this[0], name, args.slice(), MAP(body, walk) ];
+ },
+ "defun": function(name, args, body) {
+ return [ this[0], name, args.slice(), MAP(body, walk) ];
+ },
+ "if": function(conditional, t, e) {
+ return [ this[0], walk(conditional), walk(t), walk(e) ];
+ },
+ "for": function(init, cond, step, block) {
+ return [ this[0], walk(init), walk(cond), walk(step), walk(block) ];
+ },
+ "for-in": function(vvar, key, hash, block) {
+ return [ this[0], walk(vvar), walk(key), walk(hash), walk(block) ];
+ },
+ "while": function(cond, block) {
+ return [ this[0], walk(cond), walk(block) ];
+ },
+ "do": function(cond, block) {
+ return [ this[0], walk(cond), walk(block) ];
+ },
+ "return": function(expr) {
+ return [ this[0], walk(expr) ];
+ },
+ "binary": function(op, left, right) {
+ return [ this[0], op, walk(left), walk(right) ];
+ },
+ "unary-prefix": function(op, expr) {
+ return [ this[0], op, walk(expr) ];
+ },
+ "unary-postfix": function(op, expr) {
+ return [ this[0], op, walk(expr) ];
+ },
+ "sub": function(expr, subscript) {
+ return [ this[0], walk(expr), walk(subscript) ];
+ },
+ "object": function(props) {
+ return [ this[0], MAP(props, function(p){
+ return p.length == 2
+ ? [ p[0], walk(p[1]) ]
+ : [ p[0], walk(p[1]), p[2] ]; // get/set-ter
+ }) ];
+ },
+ "regexp": function(rx, mods) {
+ return [ this[0], rx, mods ];
+ },
+ "array": function(elements) {
+ return [ this[0], MAP(elements, walk) ];
+ },
+ "stat": function(stat) {
+ return [ this[0], walk(stat) ];
+ },
+ "seq": function() {
+ return [ this[0] ].concat(MAP(slice(arguments), walk));
+ },
+ "label": function(name, block) {
+ return [ this[0], name, walk(block) ];
+ },
+ "with": function(expr, block) {
+ return [ this[0], walk(expr), walk(block) ];
+ },
+ "atom": function(name) {
+ return [ this[0], name ];
+ }
+ };
+
+ var user = {};
+ var stack = [];
+ function walk(ast) {
+ if (ast == null)
+ return null;
+ try {
+ stack.push(ast);
+ var type = ast[0];
+ var gen = user[type];
+ if (gen) {
+ var ret = gen.apply(ast, ast.slice(1));
+ if (ret != null)
+ return ret;
+ }
+ gen = walkers[type];
+ return gen.apply(ast, ast.slice(1));
+ } finally {
+ stack.pop();
+ }
+ };
+
+ function with_walkers(walkers, cont){
+ var save = {}, i;
+ for (i in walkers) if (HOP(walkers, i)) {
+ save[i] = user[i];
+ user[i] = walkers[i];
+ }
+ var ret = cont();
+ for (i in save) if (HOP(save, i)) {
+ if (!save[i]) delete user[i];
+ else user[i] = save[i];
+ }
+ return ret;
+ };
+
+ return {
+ walk: walk,
+ with_walkers: with_walkers,
+ parent: function() {
+ return stack[stack.length - 2]; // last one is current node
+ },
+ stack: function() {
+ return stack;
+ }
+ };
};
-/* -----[ Scope and mangling ]----- */
-
-function Scope(parent) {
- this.names = {}; // names defined in this scope
- this.mangled = {}; // mangled names (orig.name => mangled)
- this.rev_mangled = {}; // reverse lookup (mangled => orig.name)
- this.cname = -1; // current mangled name
- this.refs = {}; // names referenced from this scope
- this.uses_with = false; // will become TRUE if with() is detected in this or any subscopes
- this.uses_eval = false; // will become TRUE if eval() is detected in this or any subscopes
- this.parent = parent; // parent scope
- this.children = []; // sub-scopes
- if (parent) {
- this.level = parent.level + 1;
- parent.children.push(this);
- } else {
- this.level = 0;
- }
-};
-
-var base54 = (function(){
- var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_";
- return function(num) {
- var ret = "";
- do {
- ret = DIGITS.charAt(num % 54) + ret;
- num = Math.floor(num / 54);
- } while (num > 0);
- return ret;
- };
-})();
-
-Scope.prototype = {
- has: function(name) {
- for (var s = this; s; s = s.parent)
- if (HOP(s.names, name))
- return s;
- },
- has_mangled: function(mname) {
- for (var s = this; s; s = s.parent)
- if (HOP(s.rev_mangled, mname))
- return s;
- },
- toJSON: function() {
- return {
- names: this.names,
- uses_eval: this.uses_eval,
- uses_with: this.uses_with
- };
- },
-
- next_mangled: function() {
- // we must be careful that the new mangled name:
- //
- // 1. doesn't shadow a mangled name from a parent
- // scope, unless we don't reference the original
- // name from this scope OR from any sub-scopes!
- // This will get slow.
- //
- // 2. doesn't shadow an original name from a parent
- // scope, in the event that the name is not mangled
- // in the parent scope and we reference that name
- // here OR IN ANY SUBSCOPES!
- //
- // 3. doesn't shadow a name that is referenced but not
- // defined (possibly global defined elsewhere).
- for (;;) {
- var m = base54(++this.cname), prior;
-
- // case 1.
- prior = this.has_mangled(m);
- if (prior && this.refs[prior.rev_mangled[m]] === prior)
- continue;
-
- // case 2.
- prior = this.has(m);
- if (prior && prior !== this && this.refs[m] === prior && !prior.has_mangled(m))
- continue;
-
- // case 3.
- if (HOP(this.refs, m) && this.refs[m] == null)
- continue;
-
- // I got "do" once. :-/
- if (!is_identifier(m))
- continue;
-
- return m;
- }
- },
- set_mangle: function(name, m) {
- this.rev_mangled[m] = name;
- return this.mangled[name] = m;
- },
- get_mangled: function(name, newMangle) {
- if (this.uses_eval || this.uses_with) return name; // no mangle if eval or with is in use
- var s = this.has(name);
- if (!s) return name; // not in visible scope, no mangle
- if (HOP(s.mangled, name)) return s.mangled[name]; // already mangled in this scope
- if (!newMangle) return name; // not found and no mangling requested
- return s.set_mangle(name, s.next_mangled());
- },
- references: function(name) {
- return name && !this.parent || this.uses_with || this.uses_eval || this.refs[name];
- },
- define: function(name, type) {
- if (name != null) {
- if (type == "var" || !HOP(this.names, name))
- this.names[name] = type || "var";
- return name;
- }
- }
-};
-
-function ast_add_scope(ast) {
-
- var current_scope = null;
- var w = ast_walker(), walk = w.walk;
- var having_eval = [];
-
- function with_new_scope(cont) {
- current_scope = new Scope(current_scope);
- var ret = current_scope.body = cont();
- ret.scope = current_scope;
- current_scope = current_scope.parent;
- return ret;
- };
-
- function define(name, type) {
- return current_scope.define(name, type);
- };
-
- function reference(name) {
- current_scope.refs[name] = true;
- };
-
- function _lambda(name, args, body) {
- var is_defun = this[0] == "defun";
- return [ this[0], is_defun ? define(name, "defun") : name, args, with_new_scope(function(){
- if (!is_defun) define(name, "lambda");
- MAP(args, function(name){ define(name, "arg") });
- return MAP(body, walk);
- })];
- };
-
- function _vardefs(type) {
- return function(defs) {
- MAP(defs, function(d){
- define(d[0], type);
- if (d[1]) reference(d[0]);
- });
- };
- };
-
- return with_new_scope(function(){
- // process AST
- var ret = w.with_walkers({
- "function": _lambda,
- "defun": _lambda,
- "label": function(name, stat) { define(name, "label") },
- "break": function(label) { if (label) reference(label) },
- "continue": function(label) { if (label) reference(label) },
- "with": function(expr, block) {
- for (var s = current_scope; s; s = s.parent)
- s.uses_with = true;
- },
- "var": _vardefs("var"),
- "const": _vardefs("const"),
- "try": function(t, c, f) {
- if (c != null) return [
- this[0],
- MAP(t, walk),
- [ define(c[0], "catch"), MAP(c[1], walk) ],
- f != null ? MAP(f, walk) : null
- ];
- },
- "name": function(name) {
- if (name == "eval")
- having_eval.push(current_scope);
- reference(name);
- }
- }, function(){
- return walk(ast);
- });
-
- // the reason why we need an additional pass here is
- // that names can be used prior to their definition.
-
- // scopes where eval was detected and their parents
- // are marked with uses_eval, unless they define the
- // "eval" name.
- MAP(having_eval, function(scope){
- if (!scope.has("eval")) while (scope) {
- scope.uses_eval = true;
- scope = scope.parent;
- }
- });
-
- // for referenced names it might be useful to know
- // their origin scope. current_scope here is the
- // toplevel one.
- function fixrefs(scope, i) {
- // do children first; order shouldn't matter
- for (i = scope.children.length; --i >= 0;)
- fixrefs(scope.children[i]);
- for (i in scope.refs) if (HOP(scope.refs, i)) {
- // find origin scope and propagate the reference to origin
- for (var origin = scope.has(i), s = scope; s; s = s.parent) {
- s.refs[i] = origin;
- if (s === origin) break;
- }
- }
- };
- fixrefs(current_scope);
-
- return ret;
- });
-
-};
-
-/* -----[ mangle names ]----- */
-
-function ast_mangle(ast, options) {
- var w = ast_walker(), walk = w.walk, scope;
- options = options || {};
-
- function get_mangled(name, newMangle) {
- if (!options.toplevel && !scope.parent) return name; // don't mangle toplevel
- if (options.except && member(name, options.except))
- return name;
- return scope.get_mangled(name, newMangle);
- };
-
- function get_define(name) {
- if (options.defines) {
- // 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", extra;
- if (name) {
- if (is_defun) name = get_mangled(name);
- else {
- extra = {};
- if (!(scope.uses_eval || scope.uses_with))
- name = extra[name] = scope.next_mangled();
- else
- extra[name] = name;
- }
- }
- body = with_scope(body.scope, function(){
- args = MAP(args, function(name){ return get_mangled(name) });
- return MAP(body, walk);
- }, extra);
- return [ this[0], name, args, body ];
- };
-
- function with_scope(s, cont, extra) {
- var _scope = scope;
- scope = s;
- if (extra) for (var i in extra) if (HOP(extra, i)) {
- s.set_mangle(i, extra[i]);
- }
- for (var i in s.names) if (HOP(s.names, i)) {
- get_mangled(i, true);
- }
- var ret = cont();
- ret.scope = s;
- scope = _scope;
- return ret;
- };
-
- function _vardefs(defs) {
- return [ this[0], MAP(defs, function(d){
- return [ get_mangled(d[0]), walk(d[1]) ];
- }) ];
- };
-
- return w.with_walkers({
- "function": _lambda,
- "defun": function() {
- // move function declarations to the top when
- // they are not in some block.
- var ast = _lambda.apply(this, arguments);
- switch (w.parent()[0]) {
- case "toplevel":
- case "function":
- case "defun":
- return MAP.at_top(ast);
- }
- return ast;
- },
- "label": function(label, stat) { return [ this[0], get_mangled(label), walk(stat) ] },
- "break": function(label) { if (label) return [ this[0], get_mangled(label) ] },
- "continue": function(label) { if (label) return [ this[0], get_mangled(label) ] },
- "var": _vardefs,
- "const": _vardefs,
- "name": function(name) {
- return get_define(name) || [ this[0], get_mangled(name) ];
- },
- "try": function(t, c, f) {
- return [ this[0],
- MAP(t, walk),
- c != null ? [ get_mangled(c[0]), MAP(c[1], walk) ] : null,
- f != null ? MAP(f, walk) : null ];
- },
- "toplevel": function(body) {
- var self = this;
- return with_scope(self.scope, function(){
- return [ self[0], MAP(body, walk) ];
- });
- }
- }, function() {
- return walk(ast_add_scope(ast));
- });
-};
-
-/* -----[
- - compress foo["bar"] into foo.bar,
- - remove block brackets {} where possible
- - join consecutive var declarations
- - various optimizations for IFs:
- - if (cond) foo(); else bar(); ==> cond?foo():bar();
- - if (cond) foo(); ==> cond&&foo();
- - if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); // also for throw
- - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
- ]----- */
-
var warn = function(){};
function best_of(ast1, ast2) {
- return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1;
+ return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1;
};
function last_stat(b) {
- if (b[0] == "block" && b[1] && b[1].length > 0)
- return b[1][b[1].length - 1];
- return b;
+ if (b[0] == "block" && b[1] && b[1].length > 0)
+ return b[1][b[1].length - 1];
+ return b;
}
function aborts(t) {
- if (t) switch (last_stat(t)[0]) {
- case "return":
- case "break":
- case "continue":
- case "throw":
- return true;
- }
+ if (t) switch (last_stat(t)[0]) {
+ case "return":
+ case "break":
+ case "continue":
+ case "throw":
+ return true;
+ }
};
function boolean_expr(expr) {
- return ( (expr[0] == "unary-prefix"
- && member(expr[1], [ "!", "delete" ])) ||
-
- (expr[0] == "binary"
- && member(expr[1], [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ])) ||
+ return ((expr[0] == "unary-prefix"
+ && member(expr[1], [ "!", "delete" ])) ||
- (expr[0] == "binary"
- && member(expr[1], [ "&&", "||" ])
- && boolean_expr(expr[2])
- && boolean_expr(expr[3])) ||
+ (expr[0] == "binary"
+ && member(expr[1], [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ])) ||
- (expr[0] == "conditional"
- && boolean_expr(expr[2])
- && boolean_expr(expr[3])) ||
+ (expr[0] == "binary"
+ && member(expr[1], [ "&&", "||" ])
+ && boolean_expr(expr[2])
+ && boolean_expr(expr[3])) ||
- (expr[0] == "assign"
- && expr[1] === true
- && boolean_expr(expr[3])) ||
+ (expr[0] == "conditional"
+ && boolean_expr(expr[2])
+ && boolean_expr(expr[3])) ||
- (expr[0] == "seq"
- && boolean_expr(expr[expr.length - 1]))
- );
-};
+ (expr[0] == "assign"
+ && expr[1] === true
+ && boolean_expr(expr[3])) ||
-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 ];
- } else {
- 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);
+ (expr[0] == "seq"
+ && boolean_expr(expr[expr.length - 1])));
};
function empty(b) {
- return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
+ return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
};
function is_string(node) {
- return (node[0] == "string" ||
- node[0] == "unary-prefix" && node[1] == "typeof" ||
- node[0] == "binary" && node[1] == "+" &&
- (is_string(node[2]) || is_string(node[3])));
-};
-
-var when_constant = (function(){
-
- var $NOT_CONSTANT = {};
-
- // this can only evaluate constant expressions. If it finds anything
- // not constant, it throws $NOT_CONSTANT.
- function evaluate(expr) {
- switch (expr[0]) {
- case "string":
- case "num":
- return expr[1];
- case "name":
- case "atom":
- switch (expr[1]) {
- case "true": return true;
- case "false": return false;
- }
- break;
- case "unary-prefix":
- switch (expr[1]) {
- case "!": return !evaluate(expr[2]);
- case "typeof": return typeof evaluate(expr[2]);
- case "~": return ~evaluate(expr[2]);
- case "-": return -evaluate(expr[2]);
- case "+": return +evaluate(expr[2]);
- }
- break;
- case "binary":
- var left = expr[2], right = expr[3];
- switch (expr[1]) {
- case "&&" : return evaluate(left) && evaluate(right);
- case "||" : return evaluate(left) || evaluate(right);
- case "|" : return evaluate(left) | evaluate(right);
- case "&" : return evaluate(left) & evaluate(right);
- case "^" : return evaluate(left) ^ evaluate(right);
- case "+" : return evaluate(left) + evaluate(right);
- case "*" : return evaluate(left) * evaluate(right);
- case "/" : return evaluate(left) / evaluate(right);
- case "-" : return evaluate(left) - evaluate(right);
- case "<<" : return evaluate(left) << evaluate(right);
- case ">>" : return evaluate(left) >> evaluate(right);
- case ">>>" : return evaluate(left) >>> evaluate(right);
- case "==" : return evaluate(left) == evaluate(right);
- case "===" : return evaluate(left) === evaluate(right);
- case "!=" : return evaluate(left) != evaluate(right);
- case "!==" : return evaluate(left) !== evaluate(right);
- case "<" : return evaluate(left) < evaluate(right);
- case "<=" : return evaluate(left) <= evaluate(right);
- case ">" : return evaluate(left) > evaluate(right);
- case ">=" : return evaluate(left) >= evaluate(right);
- case "in" : return evaluate(left) in evaluate(right);
- case "instanceof" : return evaluate(left) instanceof evaluate(right);
- }
- }
- throw $NOT_CONSTANT;
- };
-
- return function(expr, yes, no) {
- try {
- var val = evaluate(expr), ast;
- switch (typeof val) {
- case "string": ast = [ "string", val ]; break;
- case "number": ast = [ "num", val ]; break;
- case "boolean": ast = [ "name", String(val) ]; break;
- default: throw new Error("Can't handle constant of type: " + (typeof val));
- }
- return yes.call(expr, ast, val);
- } catch(ex) {
- if (ex === $NOT_CONSTANT) {
- if (expr[0] == "binary"
- && (expr[1] == "===" || expr[1] == "!==")
- && ((is_string(expr[2]) && is_string(expr[3]))
- || (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;
- }
- };
-
-})();
-
-function warn_unreachable(ast) {
- if (!empty(ast))
- warn("Dropping unreachable code: " + gen_code(ast, true));
-};
-
-function prepare_ifs(ast) {
- var w = ast_walker(), walk = w.walk;
- // In this first pass, we rewrite ifs which abort with no else with an
- // if-else. For example:
- //
- // if (x) {
- // blah();
- // return y;
- // }
- // foobar();
- //
- // is rewritten into:
- //
- // if (x) {
- // blah();
- // return y;
- // } else {
- // foobar();
- // }
- function redo_if(statements) {
- statements = MAP(statements, walk);
-
- for (var i = 0; i < statements.length; ++i) {
- var fi = statements[i];
- if (fi[0] != "if") continue;
-
- if (fi[3] && walk(fi[3])) continue;
-
- var t = walk(fi[2]);
- if (!aborts(t)) continue;
-
- var conditional = walk(fi[1]);
-
- var e_body = statements.slice(i + 1);
- var e = e_body.length == 1 ?