tco working, update playground to self-hosted newt

This commit is contained in:
2025-03-17 18:43:42 -07:00
parent 1219e8d4e5
commit 9d7e6097f3
12 changed files with 377 additions and 395 deletions

View File

@@ -6,6 +6,6 @@ echo build newt worker
esbuild src/worker.ts --bundle --format=esm > public/worker.js
esbuild src/frame.ts --bundle --format=esm > public/frame.js
echo copy newt
cp ../build/exec/newt.js public
cp ../newt.js public
cp -r static/* public
(cd samples && zip -r ../public/files.zip .)

View File

@@ -1,62 +1,5 @@
import { ZipFile } from "./zipfile";
class Buffer extends DataView {
static alloc(n: number) {
return new Buffer(new Uint8Array(n).buffer);
}
indexOf(n: number) {
return new Uint8Array(this.buffer).indexOf(n);
}
get length() {
return this.byteLength;
}
slice(start: number, end: number) {
return new Buffer(this.buffer.slice(start, end));
}
readUInt8(i: number) {
return this.getUint8(i);
}
writeUInt8(val: number, i: number) {
this.setUint8(i, val);
}
write(value: string, start: number, len: number, enc: string) {
// console.log("write", value, start, len, enc);
let buf = new TextEncoder().encode(value);
let ss = 0;
let se = Math.min(len, buf.length);
let ts = start;
for (; ss < se; ss++, ts++) this.setInt8(ts, buf[ss]);
shim.process.__lasterr.errno = 0;
return se;
}
readDoubleLE(i: number) {
return this.getFloat64(i, true);
}
readInt32LE(i: number) {
return this.getInt32(i, true);
}
writeInt32LE(val: number, i: number) {
return this.setInt32(i, val, true);
}
copy(target: Buffer, ts: number, ss: number, se: number) {
for (; ss < se; ss++, ts++) target.setInt8(ts, this.getInt8(ss));
}
static concat(bufs: Buffer[]) {
let size = bufs.reduce((a, b) => (a += b.byteLength), 0);
let rval = Buffer.alloc(size);
let off = 0;
for (let buf of bufs) {
const view = new Int8Array(rval.buffer);
view.set(new Uint8Array(buf.buffer), off);
off += buf.byteLength;
}
return rval;
}
toString() {
return new TextDecoder().decode(this);
}
}
export interface Handle {
name: string;
mode: string;
@@ -65,28 +8,14 @@ export interface Handle {
}
interface Process {
platform: string;
stdout: {
write(s: string): void;
};
argv: string[];
exit(_: number): void;
cwd(): string;
env: Record<string, string>;
__lasterr: { errno: number };
}
export interface NodeShim {
stdout: string;
archive?: ZipFile;
process: Process;
files: Record<string, Uint8Array>;
fds: Handle[];
tty: {
isatty(): number;
};
os: {
platform(): string;
};
fs: any;
}
export let shim: NodeShim = {
@@ -94,153 +23,32 @@ export let shim: NodeShim = {
archive: undefined,
stdout: "",
files: {},
fds: [],
tty: {
isatty() {
return 0;
},
},
os: {
platform() {
return "linux";
},
},
fs: {
// TODO - Idris is doing readdir, we should implement that
opendirSync(name: string) {
let fd = shim.fds.findIndex((x) => !x);
if (fd < 0) fd = shim.fds.length;
console.log("openDir", name);
shim.process.__lasterr.errno = 0;
return fd;
},
mkdirSync(name: string) {
console.log("mkdir", name);
shim.process.__lasterr.errno = 0;
return 0;
},
openSync(name: string, mode: string) {
console.log("open", name, mode);
if (name.startsWith('./')) name = name.slice(2)
let te = new TextEncoder();
let fd = shim.fds.findIndex((x) => !x);
if (fd < 0) fd = shim.fds.length;
let buf: Uint8Array;
let pos = 0;
if (mode == "w") {
buf = new Uint8Array(0);
readFileSync(name: string, encoding: string, enc?: string) {
if (name.startsWith("./")) name = name.slice(2);
let data: Uint8Array | undefined = shim.files[name]
if (!data && shim.archive?.entries[name]) {
// keep a copy of the uncompressed version for speed
data = shim.files[name] = shim.archive.getData(name)!;
}
if (data) {
return new TextDecoder().decode(data);
} else {
// TODO, we need to involve localStorage when the window does multiple files and persists
if (shim.files[name]) {
buf = shim.files[name];
} else if (shim.archive?.entries[name]) {
// keep a copy of the uncompressed version for speed
buf = shim.files[name] = shim.archive.getData(name)!;
} else {
shim.process.__lasterr.errno = 1;
throw new Error(`${name} not found`);
}
}
shim.process.__lasterr.errno = 0;
shim.fds[fd] = { buf, pos, mode, name };
// we'll mutate the pointer as stuff is read
return fd;
},
writeSync(fd: number, line: string | Buffer) {
try {
let handle = shim.fds[fd];
if (!handle) throw new Error(`bad fd ${fd}`);
let buf2: ArrayBuffer;
if (typeof line === "string") {
buf2 = new TextEncoder().encode(line);
let newbuf = new Uint8Array(handle.buf.byteLength + buf2.byteLength);
newbuf.set(new Uint8Array(handle.buf));
newbuf.set(new Uint8Array(buf2), handle.buf.byteLength);
handle.buf = newbuf;
shim.process.__lasterr.errno = 0;
} else if (line instanceof Buffer) {
let start = arguments[2];
let len = arguments[3];
buf2 = line.buffer.slice(start, start + len);
let newbuf = new Uint8Array(handle.buf.byteLength + buf2.byteLength);
newbuf.set(new Uint8Array(handle.buf));
newbuf.set(new Uint8Array(buf2), handle.buf.byteLength);
handle.buf = newbuf;
shim.process.__lasterr.errno = 0;
return len;
} else {
debugger;
throw new Error(`write ${typeof line} not implemented`);
}
} catch (e) {
debugger;
throw e;
throw new Error(`${name} not found`);
}
},
chmodSync(fn: string, mode: number) {},
fstatSync(fd: number) {
let hand = shim.fds[fd];
return { size: hand.buf.byteLength };
},
readSync(fd: number, buf: Buffer, start: number, len: number) {
let hand = shim.fds[fd];
let avail = hand.buf.length - hand.pos;
let rd = Math.min(avail, len);
let src = hand.buf;
let dest = new Uint8Array(buf.buffer);
for (let i = 0; i < rd; i++) dest[start + i] = src[hand.pos++];
return rd;
},
closeSync(fd: number) {
let handle = shim.fds[fd];
// console.log("close", handle.name);
if (handle.mode == "w") {
shim.files[handle.name] = handle.buf;
}
delete shim.fds[fd];
writeFileSync(name: string, data: string, enc?: string) {
shim.files[name] = new TextEncoder().encode(data)
},
},
process: {
platform: "linux",
argv: ["", ""],
stdout: {
write(s) {
shim.stdout += s;
},
},
exit(v: number) {
console.log("exit", v);
throw new Error(`exit ${v}`)
},
cwd() {
return "";
},
env: {
NO_COLOR: "true",
IDRIS2_CG: "javascript",
IDRIS2_PREFIX: "",
},
__lasterr: {
errno: 0,
},
// stdin: { fd: 0 },
},
}
};
// Spy on Idris' calls to see what we need to fill in
shim.fs = new Proxy(shim.fs, {
get(target, prop, receiver) {
if (prop in target) {
return (target as any)[prop];
}
let err = new Error(`IMPLEMENT fs.${String(prop)}`);
// idris support eats the exception
console.error(err);
throw err;
},
});
// we intercept require to return our fake node modules
declare global {
interface Window {

View File

@@ -6,5 +6,5 @@ declare global {
// let files: Record<string, string>;
// let process: Process;
let newtMain: () => unknown;
let Main_main: () => unknown;
}

View File

@@ -8,8 +8,6 @@ export let preload = (async function () {
let data = await res.arrayBuffer();
archive = new ZipFile(new Uint8Array(data));
let entries = archive.entries;
let count = Object.keys(entries).length;
console.log(`preloaded ${count} files`);
} else {
console.error(
`fetch of files.zip got status ${res.status}: ${res.statusText}`

View File

@@ -2,20 +2,23 @@ import { shim } from "./emul";
import { archive, preload } from "./preload";
import { CompileReq, CompileRes } from "./types";
console.log = (m) => {
shim.stdout += '\n' + m
}
const handleMessage = async function (ev: { data: CompileReq }) {
console.log("message", ev.data);
await preload;
shim.archive = archive;
let { src, fileName } = ev.data;
const outfile = "out.js";
shim.process.argv = ["", "", fileName, "-o", outfile, "--top"];
console.log("Using args", shim.process.argv);
shim.process.argv = ["browser", "newt", fileName, "-o", outfile, "--top"];
shim.files[fileName] = new TextEncoder().encode(src);
shim.files[outfile] = new TextEncoder().encode("No JS output");
shim.stdout = "";
const start = +new Date();
try {
newtMain();
Main_main();
} catch (e) {
// make it clickable in console
console.error(e);