blob: 2dfa3fc9d945a89bb27b6327e9d56a4766d20a9c [file] [log] [blame]
// Copyright 2010 The Emscripten Authors. All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
//"use strict";
// General JS utilities - things that might be useful in any JS project.
// Nothing specific to Emscripten appears here.
function safeQuote(x) {
return x.replace(/"/g, '\\"')
.replace(/'/g, "\\'");
}
function dump(item) {
try {
if (typeof item == 'object' && item !== null && item.funcData) {
var funcData = item.funcData;
item.funcData = null;
}
return '// ' + JSON.stringify(item, null, ' ').replace(/\n/g, '\n// ');
} catch(e) {
var ret = [];
for (var i in item) {
var j = item[i];
if (typeof j === 'string' || typeof j === 'number') {
ret.push(i + ': ' + j);
} else {
ret.push(i + ': [?]');
}
}
return ret.join(',\n');
} finally {
if (funcData) item.funcData = funcData;
}
}
function dumpKeys(item) {
var ret = [];
for (var i in item) {
var j = item[i];
if (typeof j === 'string' || typeof j === 'number') {
ret.push(i + ': ' + j);
} else {
ret.push(i + ': [?]');
}
}
return ret.join(', ');
}
function assertEq(a, b) {
if (a !== b) {
printErr('Stack: ' + new Error().stack);
throw 'Should have been equal: ' + a + ' : ' + b;
}
return false;
}
function assertTrue(a, msg) {
if (!a) {
msg = 'Assertion failed: ' + msg;
print(msg);
printErr('Stack: ' + new Error().stack);
throw msg;
}
}
var assert = assertTrue;
function warn(a, msg) {
if (!msg) {
msg = a;
a = false;
}
if (!a) {
printErr('warning: ' + msg);
}
}
function warnOnce(a, msg) {
if (!msg) {
msg = a;
a = false;
}
if (!a) {
if (!warnOnce.msgs) warnOnce.msgs = {};
if (msg in warnOnce.msgs) return;
warnOnce.msgs[msg] = true;
printErr('warning: ' + msg);
}
}
var abortExecution = false;
function error(msg) {
abortExecution = true;
printErr('error: ' + msg);
}
function dedup(items, ident) {
var seen = {};
if (ident) {
return items.filter(function(item) {
if (seen[item[ident]]) return false;
seen[item[ident]] = true;
return true;
});
} else {
return items.filter(function(item) {
if (seen[item]) return false;
seen[item] = true;
return true;
});
}
}
function range(size) {
var ret = [];
for (var i = 0; i < size; i++) ret.push(i);
return ret;
}
function zeros(size) {
var ret = [];
for (var i = 0; i < size; i++) ret.push(0);
return ret;
}
function spaces(size) {
var ret = '';
for (var i = 0; i < size; i++) ret += ' ';
return ret;
}
function keys(x) {
var ret = [];
for (var a in x) ret.push(a);
return ret;
}
function values(x) {
var ret = [];
for (var a in x) ret.push(x[a]);
return ret;
}
function bind(self, func) {
return function() {
func.apply(self, arguments);
};
}
function sum(x) {
return x.reduce(function(a,b) { return a+b }, 0);
}
function sumTruthy(x) {
return x.reduce(function(a,b) { return (!!a)+(!!b) }, 0);
}
function sumStringy(x) {
return x.reduce(function(a,b) { return a+b }, '');
}
function filterTruthy(x) {
return x.filter(function(y) { return !!y });
}
function loopOn(array, func) {
for (var i = 0; i < array.length; i++) {
func(i, array[i]);
}
}
// Splits out items that pass filter. Returns also the original sans the filtered
function splitter(array, filter) {
var splitOut = array.filter(filter);
var leftIn = array.filter(function(x) { return !filter(x) });
return { leftIn: leftIn, splitOut: splitOut };
}
// Usage: arrayOfArrays.reduce(concatenator, []);
function concatenator(x, y) {
return x.concat(y);
}
function mergeInto(obj, other) {
for (var i in other) {
obj[i] = other[i];
}
return obj;
}
function isNumber(x) {
// XXX this does not handle 0xabc123 etc. We should likely also do x == parseInt(x) (which handles that), and remove hack |// handle 0x... as well|
return x == parseFloat(x) || (typeof x == 'string' && x.match(/^-?\d+$/)) || x === 'NaN';
}
function isArray(x) {
try {
return typeof x === 'object' && 'length' in x && 'slice' in x;
} catch(e) {
return false;
}
}
// Flattens something like [5, 6, 'hi', [1, 'bye'], 44] into
// [5, 6, 'hi', 1, bye, 44].
function flatten(x) {
if (typeof x !== 'object') return [x];
// Avoid multiple concats by finding the size first. This is much faster
function getSize(y) {
if (typeof y !== 'object') {
return 1;
} else {
return sum(y.map(getSize));
}
}
var size = getSize(x);
var ret = new Array(size);
var index = 0;
function add(y) {
for (var i = 0; i < y.length; i++) {
if (typeof y[i] !== 'object') {
ret[index++] = y[i];
} else {
add(y[i]);
}
}
}
add(x);
assert(index == size);
return ret;
}
// Sets
function set() {
var args = typeof arguments[0] === 'object' ? arguments[0] : arguments;
var ret = {};
for (var i = 0; i < args.length; i++) {
ret[args[i]] = 0;
}
return ret;
}
var unset = keys;
function numberedSet() {
var args = typeof arguments[0] === 'object' ? arguments[0] : arguments;
var ret = {};
for (var i = 0; i < args.length; i++) {
ret[args[i]] = i;
}
return ret;
}
function setSub(x, y) {
var ret = set(keys(x));
for (var yy in y) {
if (yy in ret) {
delete ret[yy];
}
}
return ret;
}
// Intersection of 2 sets. Faster if |xx| << |yy|
function setIntersect(x, y) {
var ret = {};
for (var xx in x) {
if (xx in y) {
ret[xx] = 0;
}
}
return ret;
}
function setUnion(x, y) {
var ret = set(keys(x));
for (var yy in y) {
ret[yy] = 0;
}
return ret;
}
function setSize(x) {
var ret = 0;
for (var xx in x) ret++;
return ret;
}
function invertArray(x) {
var ret = {};
for (var i = 0; i < x.length; i++) {
ret[x[i]] = i;
}
return ret;
}
function copy(x) {
return JSON.parse(JSON.stringify(x));
}
function jsonCompare(x, y) {
return JSON.stringify(x) == JSON.stringify(y);
}
function sortedJsonCompare(x, y) {
if (x === null || typeof x !== 'object') return x === y;
for (var i in x) {
if (!sortedJsonCompare(x[i], y[i])) return false;
}
for (var i in y) {
if (!sortedJsonCompare(x[i], y[i])) return false;
}
return true;
}
function escapeJSONKey(x) {
if (/^[\d\w_]+$/.exec(x) || x[0] === '"' || x[0] === "'") return x;
assert(x.indexOf("'") < 0, 'cannot have internal single quotes in keys: ' + x);
return "'" + x + "'";
}
function stringifyWithFunctions(obj) {
if (typeof obj === 'function') return obj.toString();
if (obj === null || typeof obj !== 'object') return JSON.stringify(obj);
if (isArray(obj)) {
return '[' + obj.map(stringifyWithFunctions).join(',') + ']';
} else {
return '{' + keys(obj).map(function(key) { return escapeJSONKey(key) + ':' + stringifyWithFunctions(obj[key]) }).join(',') + '}';
}
}
function sleep(secs) {
var start = Date.now();
while (Date.now() - start < secs*1000) {};
}
function log2(x) {
return Math.log(x)/Math.LN2;
}
function isPowerOfTwo(x) {
return x > 0 && ((x & (x-1)) == 0);
}
function ceilPowerOfTwo(x) {
var ret = 1;
while (ret < x) ret <<= 1;
return ret;
}
function Benchmarker() {
var totals = {};
var ids = [], lastTime = 0;
this.start = function(id) {
var now = Date.now();
if (ids.length > 0) {
totals[ids[ids.length-1]] += now - lastTime;
}
lastTime = now;
ids.push(id);
totals[id] = totals[id] || 0;
};
this.stop = function(id) {
var now = Date.now();
assert(id === ids[ids.length-1]);
totals[id] += now - lastTime;
lastTime = now;
ids.pop();
};
this.print = function(text) {
var ids = keys(totals);
if (ids.length > 0) {
ids.sort(function(a, b) { return totals[b] - totals[a] });
printErr(text + ' times: \n' + ids.map(function(id) { return id + ' : ' + totals[id] + ' ms' }).join('\n'));
}
};
};