//==============================================================================
// Optimizer tool. This is meant to be run after the emscripten compiler has
// finished generating code. These optimizations are done on the generated
// code to further improve it. Some of the modifications also work in
// conjunction with closure compiler.
//==============================================================================
var uglify = require('../tools/eliminator/node_modules/uglify-js');
var fs = require('fs');
// Make node environment compatible with JS shells
function print(text) {
process.stdout.write(text + '\n');
}
function printErr(text) {
process.stderr.write(text + '\n');
}
function read(filename) {
if (filename[0] != '/') filename = __dirname.split('/').slice(0, -1).join('/') + '/src/' + filename;
return fs.readFileSync(filename).toString();
}
var arguments = process.argv.slice(2);
// Load some modules
eval(read('utility.js'));
// Utilities
var FUNCTION = set('defun', 'function');
var LOOP = set('do', 'while', 'for');
var LOOP_FLOW = set('break', 'continue');
var NULL_NODE = ['name', 'null'];
var UNDEFINED_NODE = ['unary-prefix', 'void', ['num', 0]];
var TRUE_NODE = ['unary-prefix', '!', ['num', 0]];
var FALSE_NODE = ['unary-prefix', '!', ['num', 1]];
var GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS:';
var generatedFunctions = null;
function setGeneratedFunctions(metadata) {
generatedFunctions = set(eval(metadata.replace(GENERATED_FUNCTIONS_MARKER, '')));
}
function isGenerated(ident) {
return ident in generatedFunctions;
}
function srcToAst(src) {
return uglify.parser.parse(src);
}
function astToSrc(ast) {
return uglify.uglify.gen_code(ast, {
ascii_only: true,
beautify: true,
indent_level: 2
});
}
// Traverses a JavaScript syntax tree rooted at the given node calling the given
// callback for each node.
// @arg node: The root of the AST.
// @arg pre: The pre to call for each node. This will be called with
// the node as the first argument and its type as the second. If true is
// returned, the traversal is stopped. If an object is returned,
// 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 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.
function traverse(node, pre, post, stack) {
var type = node[0], result, len;
var relevant = typeof type == 'string';
if (relevant) {
if (stack) len = stack.length;
var result = pre(node, type, stack);
if (result == true) return true;
if (typeof result == 'object') node = result; // Continue processing on this node
if (stack && len == stack.length) stack.push(0);
}
for (var i = 0; i < node.length; i++) {
var subnode = node[i];
if (typeof subnode == 'object' && subnode && subnode.length) {
var subresult = traverse(subnode, pre, post, stack);
if (subresult == true) return true;
if (typeof subresult == 'object') node[i] = subresult;
}
}
if (relevant) {
if (post) {
var postResult = post(node, type, stack);
result = result || postResult;
}
if (stack) stack.pop();
}
return result;
}
// Only walk through the generated functions
function traverseGenerated(ast, pre, post, stack) {
ast[1].forEach(function(node, i) {
if (node[0] == 'defun' && isGenerated(node[1])) {
traverse(node, pre, post, stack);
}
});
}
function traverseGeneratedFunctions(ast, callback) {
ast[1].forEach(function(node, i) {
if (node[0] == 'defun' && isGenerated(node[1])) {
callback(node);
}
});
}
// 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] });
} else if (type ==