neutrinoscript
Version:
Like C for Javascript
353 lines (352 loc) • 9.96 kB
JavaScript
(function (exports) {
const $M = exports;
var NODE_JS = 1;
var JS_SHELL = 2;
var BROWSER = 3;
var enable_memcheck = false;
var mode;
if (typeof process !== 'undefined') {
mode = NODE_JS;
} else if (typeof snarf !== 'undefined') {
mode = JS_SHELL;
} else {
mode = BROWSER;
}
var ck;
if (mode === NODE_JS) {
print = console.log;
ck = require('memcheck');
} else if (mode === JS_SHELL) {
ck = (load('memcheck.js'), memcheck);
} else {
ck = memcheck;
}
const MB = 1024 * 1024 | 0;
const WORD_SIZE = 4;
const SIZE = 256 * MB / WORD_SIZE;
const STACK_SIZE = 2 * MB / WORD_SIZE;
const HEAP_SIZE = SIZE - STACK_SIZE;
// debug
var log = function () {
};
// let I4, U4;
/*
+---------------+ -
0 | Heap Pointer |
1 | Stack Pointer |
+---------------+ <- Heap Pointer (HP)
| |
| | |
| HEAP | | Malloc Region
| | v
| |
+---------------+
| |
| | ^
| STACK | |
| | |
| |
+---------------+ <- Stack Pointer (SP)
*/
function memcpy(dst, src, length) {
const $U1 = $M.U1;
var _, _$1;
for (var i = 0; i < length; i = i + 1 | 0) {
$U1[_ = dst, dst = dst + 1, _] = $U1[_$1 = src, src = src + 1, _$1];
}
return dst;
}
function memcpy2(dst, src, length) {
const $U2 = $M.U2;
var _, _$1;
for (var i = 0; i < length; i = i + 1 | 0) {
$U2[_ = dst, dst = dst + 1, _] = $U2[_$1 = src, src = src + 1, _$1];
}
return dst;
}
function memcpy4(dst, src, length) {
const $U4 = $M.U4;
var _, _$1;
for (var i = 0; i < length; i = i + 1 | 0) {
$U4[_ = dst, dst = dst + 1, _] = $U4[_$1 = src, src = src + 1, _$1];
}
return dst;
}
function memset(s, b, length) {
const $U1 = $M.U1;
for (var i = 0; i < length; i = i + 1 | 0) {
$U1[s] = b;
}
}
function memset2(s, b, length) {
const $U2 = $M.U2;
for (var i = 0; i < length; i = i + 1 | 0) {
$U2[s] = b;
}
}
function memset4(s, b, length) {
const $U4 = $M.U4;
for (var i = 0; i < length; i = i + 1 | 0) {
$U4[s] = b;
}
}
var base = 0;
var freep = 0;
var inMemory = false;
function setInMemory(val) {
if (enable_memcheck) {
inMemory = val;
}
}
function shadowMemory(mem, memsize, shift) {
var handler = makeIdHandler(mem);
// override the identity get/set handlers
handler.get = function (receiver, name) {
var loc = parseInt(name, 10) << shift;
// malloc/free can get/set unallocated memory
if (!inMemory) {
if (!ck.isAddressable(loc)) {
ck.addBadAccessError(loc);
}
if (!ck.isDefined(loc)) {
ck.addUndefinedError(loc);
}
}
return mem[name];
};
handler.set = function (receiver, name, val) {
var loc = parseInt(name, 10) << shift;
// memory functions should be able to set unallocated addresses
if (!inMemory) {
if (!ck.isAddressable(loc)) {
ck.addBadAccessError(loc);
}
ck.setDefined(loc, memsize, true);
}
mem[name] = val;
return true;
};
return Proxy.create(handler);
}
function reset() {
setInMemory(true);
var M = exports.M = new ArrayBuffer(SIZE * WORD_SIZE);
if (enable_memcheck) {
ck.reset(SIZE * WORD_SIZE);
exports.U1 = shadowMemory(new Uint8Array(M), 1, 0);
exports.I1 = shadowMemory(new Int8Array(M), 1, 0);
exports.U2 = shadowMemory(new Uint16Array(M), 2, 1);
exports.I2 = shadowMemory(new Int16Array(M), 2, 1);
exports.U4 = shadowMemory(new Uint32Array(M), 4, 2);
exports.I4 = shadowMemory(new Int32Array(M), 4, 2);
exports.F4 = shadowMemory(new Float32Array(M), 4, 2);
exports.F8 = shadowMemory(new Float64Array(M), 8, 3);
} else {
exports.U1 = new Uint8Array(M);
exports.I1 = new Int8Array(M);
exports.U2 = new Uint16Array(M);
exports.I2 = new Int16Array(M);
exports.U4 = new Uint32Array(M);
exports.I4 = new Int32Array(M);
exports.F4 = new Float32Array(M);
exports.F8 = new Float64Array(M);
}
exports.U4[0] = 4;
exports.U4[1] = SIZE;
base = 2;
freep = 0;
setInMemory(false);
}
reset();
function sbrk(nBytes) {
var U4 = exports.U4;
var nWords = nBytes / 4 | 0;
if (U4[0] + nWords > HEAP_SIZE) {
return 0;
}
var address = U4[0];
U4[0] += nWords;
return address;
}
var nUnitsMin = 1024;
function morecore(nUnits) {
const $U4 = $M.U4;
if (nUnits < nUnitsMin) {
nUnits = nUnitsMin;
}
var buffer = sbrk(nUnits * 8 | 0);
if (buffer === 0) {
return 0;
}
var header = buffer;
$U4[header + 1] = nUnits;
if (enable_memcheck) {
// prevent double free recording on morecore
ck.setAlloc(header + 1 * 2 << 2, true);
// setting all the user addressable bytes as addressable in the
// shadow memory
ck.setAddressable(header + 1 * 2 << 2, nUnits, true);
}
free(header + 1 * 2 << 2);
setInMemory(true);
return freep;
}
function malloc(nBytes) {
const $U4 = $M.U4;
var p = 0, prevp = 0;
var nUnits = ((((nBytes + 8 | 0) - 1 | 0) / 8 | 0) + 1 | 0) >>> 0;
setInMemory(true);
if ((prevp = freep) === 0) {
$U4[base] = freep = prevp = base;
$U4[base + 1] = 0;
}
for (p = $U4[prevp]; true; prevp = p, p = $U4[p]) {
if ($U4[p + 1] >= nUnits) {
if ($U4[p + 1] === nUnits) {
$U4[prevp] = $U4[p];
} else {
$U4[p + 1] = ($U4[p + 1] - nUnits | 0) >>> 0;
p = p + $U4[p + 1] * 2;
$U4[p + 1] = nUnits;
}
freep = prevp;
if (enable_memcheck) {
// record that this chunck of memory can be addressed
ck.setAddressable(p + 1 * 2 << 2, nBytes, true);
ck.setAlloc(p + 1 * 2 << 2, true);
}
setInMemory(false);
return p + 1 * 2 << 2;
}
if (p === freep) {
if ((p = morecore(nUnits)) === 0) {
setInMemory(false);
return 0;
}
}
}
setInMemory(false);
return 0;
}
function free(a) {
const $U4 = $M.U4;
var ap = a;
var bp = (ap >> 2) - 1 * 2, p = 0;
if (enable_memcheck) {
setInMemory(true);
if (ck.isAlloc(ap)) {
// this byte actually was malloced before, reset it
ck.setAlloc(ap, false);
// this memory chunk is no longer addressable
ck.setAddressable(ap, $U4[bp + 1], false);
// this memory chunk is no longer defined
ck.setDefined(ap, $U4[bp + 1], false);
} else {
// this byte was never allocated, trying to free the wrong thing
ck.addDoubleFreeError(ap);
}
}
for (p = freep; !(bp > p && bp < $U4[p]); p = $U4[p]) {
if (p >= $U4[p] && (bp > p || bp < $U4[p])) {
break;
}
}
if (bp + $U4[bp + 1] * 2 === $U4[p]) {
$U4[bp + 1] = ($U4[bp + 1] + $U4[$U4[p] + 1] | 0) >>> 0;
$U4[bp] = $U4[$U4[p]];
} else {
$U4[bp] = $U4[p];
}
if (p + $U4[p + 1] * 2 == bp) {
$U4[p + 1] = ($U4[p + 1] + $U4[bp + 1] | 0) >>> 0;
$U4[p] = $U4[bp];
} else {
$U4[p] = bp;
}
freep = p;
setInMemory(false);
}
function makeIdHandler(obj) {
return {
getOwnPropertyDescriptor: function (name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
// a trapping proxy's properties must always be configurable
if (desc !== undefined) {
desc.configurable = true;
}
return desc;
},
getPropertyDescriptor: function (name) {
var desc = Object.getPropertyDescriptor(obj, name);
// not in ES5
// a trapping proxy's properties must always be configurable
if (desc !== undefined) {
desc.configurable = true;
}
return desc;
},
getOwnPropertyNames: function () {
return Object.getOwnPropertyNames(obj);
},
getPropertyNames: function () {
return Object.getPropertyNames(obj);
},
defineProperty: function (name, desc) {
Object.defineProperty(obj, name, desc);
},
delete: function (name) {
return delete obj[name];
},
fix: function () {
if (Object.isFrozen(obj)) {
return Object.getOwnPropertyNames(obj).map(function (name) {
return Object.getOwnPropertyDescriptor(obj, name);
});
}
// As long as obj is not frozen, the proxy won't allow itself to be fixed
return undefined;
},
has: function (name) {
return name in obj;
},
hasOwn: function (name) {
return Object.prototype.hasOwnProperty.call(obj, name);
},
get: function (receiver, name) {
return obj[name];
},
set: function (receiver, name, val) {
obj[name] = val;
return true;
},
enumerate: function () {
var result = [];
for (var name in obj) {
result.push(name);
}
;
return result;
},
keys: function () {
return Object.keys(obj);
}
};
}
exports.reset = reset;
exports.memcpy = memcpy;
exports.memcpy2 = memcpy2;
exports.memcpy4 = memcpy4;
exports.memset = memset;
exports.memset2 = memset2;
exports.memset4 = memset4;
exports.malloc = malloc;
exports.free = free;
exports.set_memcheck = function (val) {
enable_memcheck = val;
reset();
};
exports.memcheck = ck;
exports.memcheck_call_pop = ck.memcheck_call_pop;
exports.memcheck_call_push = ck.memcheck_call_push;
exports.memcheck_call_reset = ck.memcheck_call_reset;
}.call(this, typeof exports === 'undefined' ? memory = {} : exports));