Files
Kameleon 1220a577a2 Update to psfree-1.5rc1
900 Port in process: DO NOT USE IT.
2025-05-10 09:34:58 -06:00

431 lines
12 KiB
JavaScript

/* Copyright (C) 2023-2025 anonymous
This file is part of PSFree.
PSFree is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
PSFree is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { Int, lohi_from_one } from './int64.mjs';
import { view_m_vector, view_m_length } from './offset.mjs';
export let mem = null;
// cache some constants
const off_vector = view_m_vector / 4;
const off_vector2 = (view_m_vector + 4) / 4;
const isInteger = Number.isInteger;
function init_module(memory) {
mem = memory;
}
function add_and_set_addr(mem, offset, base_lo, base_hi) {
const values = lohi_from_one(offset);
const main = mem._main;
const low = base_lo + values[0];
// no need to use ">>> 0" to convert to unsigned here
main[off_vector] = low;
main[off_vector2] = base_hi + values[1] + (low > 0xffffffff);
}
export class Addr extends Int {
read8(offset) {
const m = mem;
if (isInteger(offset) && 0 <= offset && offset <= 0xffffffff) {
m._set_addr_direct(this);
} else {
add_and_set_addr(m, offset, this.lo, this.hi);
offset = 0;
}
return m.read8_at(offset);
}
read16(offset) {
const m = mem;
if (isInteger(offset) && 0 <= offset && offset <= 0xffffffff) {
m._set_addr_direct(this);
} else {
add_and_set_addr(m, offset, this.lo, this.hi);
offset = 0;
}
return m.read16_at(offset);
}
read32(offset) {
const m = mem;
if (isInteger(offset) && 0 <= offset && offset <= 0xffffffff) {
m._set_addr_direct(this);
} else {
add_and_set_addr(m, offset, this.lo, this.hi);
offset = 0;
}
return m.read32_at(offset);
}
read64(offset) {
const m = mem;
if (isInteger(offset) && 0 <= offset && offset <= 0xffffffff) {
m._set_addr_direct(this);
} else {
add_and_set_addr(m, offset, this.lo, this.hi);
offset = 0;
}
return m.read64_at(offset);
}
readp(offset) {
const m = mem;
if (isInteger(offset) && 0 <= offset && offset <= 0xffffffff) {
m._set_addr_direct(this);
} else {
add_and_set_addr(m, offset, this.lo, this.hi);
offset = 0;
}
return m.readp_at(offset);
}
write8(offset, value) {
const m = mem;
if (isInteger(offset) && 0 <= offset && offset <= 0xffffffff) {
m._set_addr_direct(this);
} else {
add_and_set_addr(m, offset, this.lo, this.hi);
offset = 0;
}
m.write8_at(offset, value);
}
write16(offset, value) {
const m = mem;
if (isInteger(offset) && 0 <= offset && offset <= 0xffffffff) {
m._set_addr_direct(this);
} else {
add_and_set_addr(m, offset, this.lo, this.hi);
offset = 0;
}
m.write16_at(offset, value);
}
write32(offset, value) {
const m = mem;
if (isInteger(offset) && 0 <= offset && offset <= 0xffffffff) {
m._set_addr_direct(this);
} else {
add_and_set_addr(m, offset, this.lo, this.hi);
offset = 0;
}
m.write32_at(offset, value);
}
write64(offset, value) {
const m = mem;
if (isInteger(offset) && 0 <= offset && offset <= 0xffffffff) {
m._set_addr_direct(this);
} else {
add_and_set_addr(m, offset, this.lo, this.hi);
offset = 0;
}
m.write64_at(offset, value);
}
}
// expected:
// * main - Uint32Array whose m_vector points to worker
// * worker - DataView
//
// addrof()/fakeobj() expectations:
// * obj - has a "addr" property and a 0 index.
// * addr_addr - Int, the address of the slot of obj.addr
// * fake_addr - Int, the address of the slot of obj[0]
//
// a valid example for "obj" is "{addr: null, 0: 0}". note that this example
// has [0] be 0 so that the butterfly's indexing type is ArrayWithInt32. this
// prevents the garbage collector from incorrectly treating the slot's value as
// a JSObject and then crash
//
// the relative read/write methods expect the offset to be a unsigned 32-bit
// integer
export class Memory {
constructor(main, worker, obj, addr_addr, fake_addr) {
this._main = main;
this._worker = worker;
this._obj = obj;
this._addr_low = addr_addr.lo;
this._addr_high = addr_addr.hi;
this._fake_low = fake_addr.lo;
this._fake_high = fake_addr.hi;
main[view_m_length / 4] = 0xffffffff;
init_module(this);
const off_mvec = view_m_vector;
// use this to create WastefulTypedArrays to avoid a GC crash
const buf = new ArrayBuffer(0);
const src = new Uint8Array(buf);
const sset = new Uint32Array(buf);
const sset_p = this.addrof(sset);
sset_p.write64(off_mvec, this.addrof(src).add(off_mvec));
sset_p.write32(view_m_length, 3);
this._cpysrc = src;
this._src_setter = sset;
const dst = new Uint8Array(buf);
const dset = new Uint32Array(buf);
const dset_p = this.addrof(dset);
dset_p.write64(off_mvec, this.addrof(dst).add(off_mvec));
dset_p.write32(view_m_length, 3);
dset[2] = 0xffffffff;
this._cpydst = dst;
this._dst_setter = dset;
}
// dst and src may overlap
cpy(dst, src, len) {
if (!(isInteger(len) && 0 <= len && len <= 0xffffffff)) {
throw TypeError('len not a unsigned 32-bit integer');
}
const dvals = lohi_from_one(dst);
const svals = lohi_from_one(src);
const dset = this._dst_setter;
const sset = this._src_setter;
dset[0] = dvals[0];
dset[1] = dvals[1];
sset[0] = svals[0];
sset[1] = svals[1];
sset[2] = len;
this._cpydst.set(this._cpysrc);
}
// allocate Garbage Collector managed memory. returns [address_of_memory,
// backer]. backer is the JSCell that is keeping the returned memory alive,
// you can drop it once you have another GC object reference the address.
// the backer is an implementation detail. don't use it to mutate the
// memory
gc_alloc(size) {
if (!isInteger(size)) {
throw TypeError('size not a integer');
}
if (size < 0) {
throw RangeError('size is negative');
}
const fastLimit = 1000;
size = (size + 7 & ~7) >> 3;
if (size > fastLimit) {
throw RangeError('size is too large');
}
const backer = new Float64Array(size);
return [mem.addrof(backer).readp(view_m_vector), backer];
}
fakeobj(addr) {
const values = lohi_from_one(addr);
const worker = this._worker;
const main = this._main;
main[off_vector] = this._fake_low;
main[off_vector2] = this._fake_high;
worker.setUint32(0, values[0], true);
worker.setUint32(4, values[1], true);
return this._obj[0];
}
addrof(object) {
// typeof considers null as a object. blacklist it as it isn't a
// JSObject
if (object === null
|| (typeof object !== 'object' && typeof object !== 'function')
) {
throw TypeError('argument not a JS object');
}
const obj = this._obj;
const worker = this._worker;
const main = this._main;
obj.addr = object;
main[off_vector] = this._addr_low;
main[off_vector2] = this._addr_high;
const res = new Addr(
worker.getUint32(0, true),
worker.getUint32(4, true),
);
obj.addr = null;
return res;
}
// expects addr to be a Int
_set_addr_direct(addr) {
const main = this._main;
main[off_vector] = addr.lo;
main[off_vector2] = addr.hi;
}
set_addr(addr) {
const values = lohi_from_one(addr);
const main = this._main;
main[off_vector] = values[0];
main[off_vector2] = values[1];
}
get_addr() {
const main = this._main;
return new Addr(main[off_vector], main[off_vector2]);
}
read8(addr) {
this.set_addr(addr);
return this._worker.getUint8(0);
}
read16(addr) {
this.set_addr(addr);
return this._worker.getUint16(0, true);
}
read32(addr) {
this.set_addr(addr);
return this._worker.getUint32(0, true);
}
read64(addr) {
this.set_addr(addr);
const worker = this._worker;
return new Int(worker.getUint32(0, true), worker.getUint32(4, true));
}
// returns a pointer instead of an Int
readp(addr) {
this.set_addr(addr);
const worker = this._worker;
return new Addr(worker.getUint32(0, true), worker.getUint32(4, true));
}
read8_at(offset) {
if (!isInteger(offset)) {
throw TypeError('offset not a integer');
}
return this._worker.getUint8(offset);
}
read16_at(offset) {
if (!isInteger(offset)) {
throw TypeError('offset not a integer');
}
return this._worker.getUint16(offset, true);
}
read32_at(offset) {
if (!isInteger(offset)) {
throw TypeError('offset not a integer');
}
return this._worker.getUint32(offset, true);
}
read64_at(offset) {
if (!isInteger(offset)) {
throw TypeError('offset not a integer');
}
const worker = this._worker;
return new Int(
worker.getUint32(offset, true),
worker.getUint32(offset + 4, true),
);
}
readp_at(offset) {
if (!isInteger(offset)) {
throw TypeError('offset not a integer');
}
const worker = this._worker;
return new Addr(
worker.getUint32(offset, true),
worker.getUint32(offset + 4, true),
);
}
write8(addr, value) {
this.set_addr(addr);
this._worker.setUint8(0, value);
}
write16(addr, value) {
this.set_addr(addr);
this._worker.setUint16(0, value, true);
}
write32(addr, value) {
this.set_addr(addr);
this._worker.setUint32(0, value, true);
}
write64(addr, value) {
const values = lohi_from_one(value);
this.set_addr(addr);
const worker = this._worker;
worker.setUint32(0, values[0], true);
worker.setUint32(4, values[1], true);
}
write8_at(offset, value) {
if (!isInteger(offset)) {
throw TypeError('offset not a integer');
}
this._worker.setUint8(offset, value);
}
write16_at(offset, value) {
if (!isInteger(offset)) {
throw TypeError('offset not a integer');
}
this._worker.setUint16(offset, value, true);
}
write32_at(offset, value) {
if (!isInteger(offset)) {
throw TypeError('offset not a integer');
}
this._worker.setUint32(offset, value, true);
}
write64_at(offset, value) {
if (!isInteger(offset)) {
throw TypeError('offset not a integer');
}
const values = lohi_from_one(value);
const worker = this._worker;
worker.setUint32(offset, values[0], true);
worker.setUint32(offset + 4, values[1], true);
}
}