fix for js optimizer not noticing globals are modified

This commit is contained in:
Alon Zakai 2011-11-19 22:14:56 -08:00
Родитель 9a004a8fa1
Коммит 580d7f6408
3 изменённых файлов: 71 добавлений и 53 удалений

Просмотреть файл

@ -35,7 +35,7 @@ var FUNCTION = set('defun', 'function');
// it replaces the passed node in the tree.
// @arg post: A callback to call after traversing all children.
// @arg stack: If true, a stack will be implemented: If pre does not push on
// the stack, we push a null. We pop when we leave the node. The
// the stack, we push a 0. We pop when we leave the node. The
// stack is passed as a third parameter to the callbacks.
// @returns: If the root node was replaced, the new root node. If the traversal
// was stopped, true. Otherwise undefined.
@ -46,7 +46,7 @@ function traverse(node, pre, post, stack) {
if (stack) len = stack.length;
var result = pre(node, type, stack);
if (result == true) return true;
if (stack && len == stack.length) stack.push(null);
if (stack && len == stack.length) stack.push(0);
}
for (var i = 0; i < node.length; i++) {
var subnode = node[i];
@ -66,45 +66,8 @@ function traverse(node, pre, post, stack) {
return result;
}
function makeEmptyNode() {
return ['toplevel', []]
}
// Passes
// Undos closure's creation of global variables with values true, false,
// undefined, null. These cut down on size, but do not affect gzip size
// and make JS engine's lives slightly harder (?)
function unGlobalize(ast) {
assert(ast[0] == 'toplevel');
var values = {};
// Find global renamings of the relevant values
ast[1].forEach(function(node, i) {
if (node[0] != 'var') return;
node[1] = node[1].filter(function(varItem) {
var key = varItem[0];
var value = varItem[1];
if (!value) return true;
if (value[0] == 'name' && value[1] == 'null') {
values[key] = ['name','null'];
return false;
} else if (value[0] == 'unary-prefix' && value[1] == 'void' && value[2] && value[2].length == 2 && value[2][0] == 'num' && value[2][1] == 0){
values[key] = ['unary-prefix','void',['num',0]];
return false;
} else if (value[0] == 'unary-prefix' && value[1] == '!' && value[2] && value[2].length == 2 && value[2][0] == 'num' && value[2][1] == 0){
values[key] = ['unary-prefix','!',['num',0]];
return false;
} else if (value[0] == 'unary-prefix' && value[1] == '!' && value[2] && value[2].length == 2 && value[2][0] == 'num' && value[2][1] == 1){
values[key] = ['unary-prefix','!',['num',1]];
return false;
}
return true;
});
if (node[1].length == 0) {
ast[1][i] = makeEmptyNode();
}
});
// Walk the ast, with an understanding of JS variable scope (i.e., function-level scoping)
// Walk the ast in a simple way, with an understanding of which JS variables are defined)
function traverseWithVariables(ast, callback) {
traverse(ast, function(node, type, stack) {
if (type in FUNCTION) {
stack.push({ type: 'function', vars: node[2] });
@ -121,21 +84,72 @@ function unGlobalize(ast) {
var allVars = stack.map(function(item) { return item ? item.vars : [] }).reduce(concatenator, []); // FIXME dictionary for speed?
traverse(node, function(node, type, stack) {
// Be careful not to look into our inner functions. They have already been processed.
if (type == 'name' && sum(stack) == 1) {
var ident = node[1];
if (ident in values && allVars.indexOf(ident) < 0) {
return copy(values[ident]);
}
} else if (type in FUNCTION) {
stack.push(1);
} else {
stack.push(0);
}
if (sum(stack) > 1) return;
if (type in FUNCTION) stack.push(1);
return callback(node, type, allVars);
}, null, []);
}
}, []);
}
function makeEmptyNode() {
return ['toplevel', []]
}
// Passes
// Undos closure's creation of global variables with values true, false,
// undefined, null. These cut down on size, but do not affect gzip size
// and make JS engine's lives slightly harder (?)
function unGlobalize(ast) {
assert(ast[0] == 'toplevel');
var values = {};
// Find global renamings of the relevant values
ast[1].forEach(function(node, i) {
if (node[0] != 'var') return;
node[1] = node[1].filter(function(varItem, j) {
var ident = varItem[0];
var value = varItem[1];
if (!value) return true;
var possible = false;
if (value[0] == 'name' && value[1] == 'null') {
possible = true;
} else if (value[0] == 'unary-prefix' && value[1] == 'void' && value[2] && value[2].length == 2 && value[2][0] == 'num' && value[2][1] == 0) {
possible = true;
} else if (value[0] == 'unary-prefix' && value[1] == '!' && value[2] && value[2].length == 2 && value[2][0] == 'num' && value[2][1] == 0) {
possible = true;
} else if (value[0] == 'unary-prefix' && value[1] == '!' && value[2] && value[2].length == 2 && value[2][0] == 'num' && value[2][1] == 1) {
possible = true;
}
if (!possible) return true;
// Make sure there are no assignments to this variable. (This isn't fast, we traverse many times..)
ast[1][i][1][j] = makeEmptyNode();
var assigned = false;
traverseWithVariables(ast, function(node, type, allVars) {
if (type == 'assign' && node[2][0] == 'name' && node[2][1] == ident) assigned = true;
});
ast[1][i][1][j] = [ident, value];
if (!assigned) {
values[ident] = value;
return false;
}
return true;
});
if (node[1].length == 0) {
ast[1][i] = makeEmptyNode();
}
});
traverseWithVariables(ast, function(node, type, allVars) {
if (type == 'name') {
var ident = node[1];
if (ident in values && allVars.indexOf(ident) < 0) {
return copy(values[ident]);
}
}
});
}
// Main
var src = fs.readFileSync('/dev/stdin').toString();

Просмотреть файл

@ -1,6 +1,6 @@
var c;
var b = 5;
var s3 = !0;
function abc() {
var cheez = void 0;
var fleefl;
@ -11,6 +11,7 @@ function abc() {
var waka3 = void 0, flake3 = 5, marfoosh3 = void 0;
var waka4 = void 0, flake4 = void 0, marfoosh4 = 5;
var test1 = !0, test2 = !1, test3 = null, test4 = z, test5 = b, test6 = void 0;
s3 = 9;
}
function xyz() {
var x = 52;
@ -24,6 +25,7 @@ function xyz() {
var i = x, j = null, k = 5, l = t, m = s2;
var s2 = 8;
});
var patama = s3;
}
function xyz2(x) {
var cheez = x;

Просмотреть файл

@ -1,6 +1,6 @@
var c;
var b = 5, x = void 0, y = null, t = !0;
var s = !1, s2 = !1;
var s = !1, s2 = !1, s3 = !0;
function abc() {
var cheez = x;
var fleefl;
@ -11,6 +11,7 @@ function abc() {
var waka3 = x, flake3 = 5, marfoosh3 = x;
var waka4 = x, flake4 = x, marfoosh4 = 5;
var test1 = t, test2 = s, test3 = y, test4 = z, test5 = b, test6 = x;
s3 = 9;
}
function xyz() {
var x = 52;
@ -24,6 +25,7 @@ function xyz() {
var i = x, j = y, k = 5, l = t, m = s2;
var s2 = 8;
};
var patama = s3;
}
function xyz2(x) {
var cheez = x;