aboutsummaryrefslogtreecommitdiff
path: root/tools/js-optimizer.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/js-optimizer.js')
-rw-r--r--tools/js-optimizer.js94
1 files changed, 64 insertions, 30 deletions
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index d84c664a..7094cdfc 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -15,6 +15,7 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR
if (ENVIRONMENT_IS_NODE) {
// Expose functionality in the same simple way that the shells work
+ // Note that we pollute the global namespace here, otherwise we break in node
print = function(x) {
process['stdout'].write(x + '\n');
};
@@ -33,26 +34,30 @@ if (ENVIRONMENT_IS_NODE) {
return ret;
};
+ load = function(f) {
+ globalEval(read(f));
+ };
+
arguments_ = process['argv'].slice(2);
} else if (ENVIRONMENT_IS_SHELL) {
// Polyfill over SpiderMonkey/V8 differences
if (!this['read']) {
- read = function(f) { snarf(f) };
+ this['read'] = function(f) { snarf(f) };
}
- if (!this['arguments']) {
+ if (typeof scriptArgs != 'undefined') {
arguments_ = scriptArgs;
- } else {
+ } else if (typeof arguments != 'undefined') {
arguments_ = arguments;
}
} else if (ENVIRONMENT_IS_WEB) {
- print = printErr = function(x) {
+ this['print'] = printErr = function(x) {
console.log(x);
};
- read = function(url) {
+ this['read'] = function(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send(null);
@@ -65,7 +70,7 @@ if (ENVIRONMENT_IS_NODE) {
} else if (ENVIRONMENT_IS_WORKER) {
// We can do very little here...
- load = importScripts;
+ this['load'] = importScripts;
} else {
throw 'Unknown runtime environment. Where are we?';
@@ -76,17 +81,17 @@ function globalEval(x) {
}
if (typeof load == 'undefined' && typeof read != 'undefined') {
- load = function(f) {
+ this['load'] = function(f) {
globalEval(read(f));
};
}
if (typeof printErr === 'undefined') {
- printErr = function(){};
+ this['printErr'] = function(){};
}
if (typeof print === 'undefined') {
- print = printErr;
+ this['print'] = printErr;
}
// *** Environment setup code ***
@@ -239,7 +244,7 @@ function emptyNode() {
// Dump the AST. Useful for debugging. For example,
// node tools/js-optimizer.js ABSOLUTE_PATH_TO_FILE dumpAst
function dumpAst(ast) {
- printErr(JSON.stringify(ast));
+ printErr(JSON.stringify(ast, null, ' '));
}
function dumpSrc(ast) {
@@ -576,16 +581,10 @@ function optimizeShiftsInternal(ast, conservative) {
var name = node[2][1];
var data = vars[name];
var parent = stack[stack.length-3];
- var parentIndex;
- if (parent[0] == 'defun') {
- parentIndex = 3;
- } else if (parent[0] == 'block') {
- parentIndex = 1;
- } else {
- throw 'Invalid parent for assign-shift: ' + dump(parent);
- }
- var i = parent[parentIndex].indexOf(stack[stack.length-2]);
- parent[parentIndex].splice(i+1, 0, ['stat', ['assign', true, ['name', name + '$s' + data.primaryShift], ['binary', '>>', ['name', name, true], ['num', data.primaryShift]]]]);
+ var statements = getStatements(parent);
+ assert(statements, 'Invalid parent for assign-shift: ' + dump(parent));
+ var i = statements.indexOf(stack[stack.length-2]);
+ statements.splice(i+1, 0, ['stat', ['assign', true, ['name', name + '$s' + data.primaryShift], ['binary', '>>', ['name', name, true], ['num', data.primaryShift]]]]);
} else if (node[0] == 'var') {
var args = node[1];
for (var i = 0; i < args.length; i++) {
@@ -801,18 +800,24 @@ function vacuum(ast) {
if (node[0] == 'block' && (!node[1] || (typeof node[1] != 'object') || node[1].length == 0 || (node[1].length == 1 && isEmpty(node[1])))) return true;
return false;
}
- function simplifyList(node, i) {
+ function simplifyList(node, si) {
var changed = false;
- var pre = node[i].length;
- node[i] = node[i].filter(function(node) { return !isEmpty(node) });
- if (node[i].length < pre) changed = true;
- // Also, seek blocks with single items we can simplify
- node[i] = node[i].map(function(subNode) {
- if (subNode[0] == 'block' && typeof subNode[1] == 'object' && subNode[1].length == 1 && subNode[1][0][0] == 'if') {
- return subNode[1][0];
+ // Merge block items into this list, thus removing unneeded |{ .. }|'s
+ var statements = node[si];
+ var i = 0;
+ while (i < statements.length) {
+ var subNode = statements[i];
+ if (subNode[0] == 'block') {
+ statements.splice.apply(statements, [i, 1].concat(subNode[1] || []));
+ changed = true;
+ } else {
+ i++;
}
- return subNode;
- });
+ }
+ // Remove empty items
+ var pre = node[si].length;
+ node[si] = node[si].filter(function(node) { return !isEmpty(node) });
+ if (node[si].length < pre) changed = true;
if (changed) {
return node;
}
@@ -1002,6 +1007,35 @@ function hoistMultiples(ast) {
});
vacuum(ast);
+
+ // Afterpass: Reduce
+ // if (..) { .. break|continue } else { .. }
+ // to
+ // if (..) { .. break|continue } ..
+ traverseGenerated(ast, function(container, type) {
+ var statements = getStatements(container);
+ if (!statements) return;
+ for (var i = 0; i < statements.length; i++) {
+ var node = statements[i];
+ if (node[0] == 'if' && node[2][0] == 'block' && node[3] && node[3][0] == 'block') {
+ var stat1 = node[2][1], stat2 = node[3][1];
+ // If break|continue in the latter and not the former, reverse them
+ if (!(stat1[stat1.length-1][0] in LOOP_FLOW) && (stat2[stat2.length-1][0] in LOOP_FLOW)) {
+ var temp = node[3];
+ node[3] = node[2];
+ node[2] = temp;
+ node[1] = ['unary-prefix', '!', node[1]];
+ simplifyNotComps(node[1]); // bake the ! into the expression
+ stat1 = node[2][1];
+ stat2 = node[3][1];
+ }
+ if (stat1[stat1.length-1][0] in LOOP_FLOW) {
+ statements.splice.apply(statements, [i+1, 0].concat(stat2));
+ node[3] = null;
+ }
+ }
+ }
+ });
}
// Simplifies loops