Update to Hoot 0.5.0.
This commit is contained in:
parent
b628b655cf
commit
825fdbc372
3 changed files with 339 additions and 40 deletions
24
manifest.scm
24
manifest.scm
|
@ -1,28 +1,8 @@
|
|||
(use-modules (guix git-download)
|
||||
(guix packages)
|
||||
(gnu packages autotools)
|
||||
(gnu packages base)
|
||||
(gnu packages compression)
|
||||
(gnu packages guile)
|
||||
(gnu packages guile-xyz)
|
||||
(gnu packages pkg-config)
|
||||
(gnu packages texinfo))
|
||||
(gnu packages guile-xyz))
|
||||
|
||||
(define guile-hoot-next
|
||||
(let ((commit "66eca43e7fc0478b265c7826b7e52eec866e0b21")
|
||||
(revision "1"))
|
||||
(package
|
||||
(inherit guile-hoot)
|
||||
(version (git-version "0.4.1" revision commit))
|
||||
(source (origin
|
||||
(method git-fetch)
|
||||
(uri (git-reference
|
||||
(url "https://gitlab.com/spritely/guile-hoot.git")
|
||||
(commit commit)))
|
||||
(file-name (git-file-name "guile-hoot" version))
|
||||
(sha256
|
||||
(base32 "0pd81wg6adzf1kvmgcxylailzwa0d5vdrhfpy4d54ddl31j6h824"))))
|
||||
(native-inputs
|
||||
(list autoconf automake pkg-config texinfo)))))
|
||||
|
||||
(packages->manifest (list guile-next guile-hoot-next gnu-make zip))
|
||||
(packages->manifest (list guile-next guile-hoot gnu-make zip))
|
||||
|
|
355
reflect.js
355
reflect.js
|
@ -141,7 +141,7 @@ class Port extends HeapObject { toString() { return "#<port>"; } }
|
|||
class Struct extends HeapObject { toString() { return "#<struct>"; } }
|
||||
|
||||
function instantiate_streaming(path, imports) {
|
||||
if (typeof fetch !== 'undefined')
|
||||
if (typeof fetch !== 'undefined' && typeof window !== 'undefined')
|
||||
return WebAssembly.instantiateStreaming(fetch(path), imports);
|
||||
let bytes;
|
||||
if (typeof read !== 'undefined') {
|
||||
|
@ -155,6 +155,168 @@ function instantiate_streaming(path, imports) {
|
|||
return WebAssembly.instantiate(bytes, imports);
|
||||
}
|
||||
|
||||
class IterableWeakSet {
|
||||
#array;
|
||||
#set;
|
||||
constructor() { this.#array = []; this.#set = new WeakSet; }
|
||||
#kill(i) {
|
||||
let tail = this.#array.pop();
|
||||
if (i < this.#array.length)
|
||||
this.#array[i] = tail;
|
||||
}
|
||||
#get(i) {
|
||||
if (i >= this.#array.length)
|
||||
return null;
|
||||
let obj = this.#array[i].deref();
|
||||
if (obj)
|
||||
return obj;
|
||||
this.#kill(i);
|
||||
return null;
|
||||
}
|
||||
#cleanup() {
|
||||
let i = 0;
|
||||
while (this.#get(i)) i++;
|
||||
}
|
||||
has(x) { return this.#set.has(x); }
|
||||
add(x) {
|
||||
if (this.has(x))
|
||||
return;
|
||||
if (this.#array.length % 32 == 0)
|
||||
this.#cleanup();
|
||||
this.#set.add(x);
|
||||
this.#array.push(new WeakRef(x));
|
||||
}
|
||||
delete(x) {
|
||||
if (!this.has(x))
|
||||
return;
|
||||
this.#set.delete(x);
|
||||
let i = 0;
|
||||
while (this.#get(i) != x) i++;
|
||||
this.#kill(i);
|
||||
}
|
||||
*[Symbol.iterator]() {
|
||||
for (let i = 0, x; x = this.#get(i); i++)
|
||||
yield x;
|
||||
}
|
||||
}
|
||||
|
||||
// See:
|
||||
// https://github.com/tc39/proposal-weakrefs?tab=readme-ov-file#iterable-weakmaps
|
||||
class IterableWeakMap {
|
||||
#weakMap = new WeakMap();
|
||||
#refSet = new Set();
|
||||
#finalizationGroup = new FinalizationRegistry(IterableWeakMap.#cleanup);
|
||||
|
||||
static #cleanup({ set, ref }) {
|
||||
set.delete(ref);
|
||||
}
|
||||
|
||||
constructor(iterable) {
|
||||
for (const [key, value] of iterable) {
|
||||
this.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
const ref = new WeakRef(key);
|
||||
|
||||
this.#weakMap.set(key, { value, ref });
|
||||
this.#refSet.add(ref);
|
||||
this.#finalizationGroup.register(key, {
|
||||
set: this.#refSet,
|
||||
ref
|
||||
}, ref);
|
||||
}
|
||||
|
||||
get(key) {
|
||||
const entry = this.#weakMap.get(key);
|
||||
return entry && entry.value;
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
const entry = this.#weakMap.get(key);
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.#weakMap.delete(key);
|
||||
this.#refSet.delete(entry.ref);
|
||||
this.#finalizationGroup.unregister(entry.ref);
|
||||
return true;
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
for (const ref of this.#refSet) {
|
||||
const key = ref.deref();
|
||||
if (!key) continue;
|
||||
const { value } = this.#weakMap.get(key);
|
||||
yield [key, value];
|
||||
}
|
||||
}
|
||||
|
||||
entries() {
|
||||
return this[Symbol.iterator]();
|
||||
}
|
||||
|
||||
*keys() {
|
||||
for (const [key, value] of this) {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
|
||||
*values() {
|
||||
for (const [key, value] of this) {
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
}
|
||||
// class IterableWeakMap {
|
||||
// #array;
|
||||
// #map;
|
||||
// constructor() { this.#array = []; this.#map = new WeakMap; }
|
||||
// #kill(i) {
|
||||
// let tail = this.#array.pop();
|
||||
// if (i < this.#array.length)
|
||||
// this.#array[i] = tail;
|
||||
// }
|
||||
// #geti(i) {
|
||||
// // If a dead weak ref is at i then kill it and try again with
|
||||
// // the next weak ref.
|
||||
// while (true) {
|
||||
// if (i >= this.#array.length)
|
||||
// return null;
|
||||
// let obj = this.#array[i].deref();
|
||||
// if (obj)
|
||||
// return obj;
|
||||
// this.#kill(i);
|
||||
// }
|
||||
// }
|
||||
// #cleanup() {
|
||||
// let i = 0;
|
||||
// while (this.#get(i)) i++;
|
||||
// }
|
||||
// has(k) { return this.#map.has(k); }
|
||||
// get(k) { return this.#map.get(k); }
|
||||
// set(k, v) {
|
||||
// if (this.#array.length % 32 == 0)
|
||||
// this.#cleanup();
|
||||
// this.#map.set(k, v);
|
||||
// this.#array.push(new WeakRef(k));
|
||||
// }
|
||||
// delete(k) {
|
||||
// if (!this.has(k))
|
||||
// return;
|
||||
// this.#map.delete(k);
|
||||
// let i = 0;
|
||||
// while (this.#geti(i) != k) i++;
|
||||
// this.#kill(i);
|
||||
// }
|
||||
// *[Symbol.iterator]() {
|
||||
// for (let i = 0, k; k = this.#geti(i); i++)
|
||||
// yield [k, this.get(k)];
|
||||
// }
|
||||
// }
|
||||
|
||||
class Scheme {
|
||||
#instance;
|
||||
#abi;
|
||||
|
@ -170,9 +332,11 @@ class Scheme {
|
|||
debug_str_scm: (x, y) => {
|
||||
console.log(`reflect debug: ${x}: #<scm>`);
|
||||
},
|
||||
code_source(x) { return ['???', 0, 0]; }
|
||||
};
|
||||
let reflect_wasm = reflect_wasm_dir + '/reflect.wasm';
|
||||
let rt = {
|
||||
quit(status) { throw new SchemeQuitError(status); },
|
||||
die(tag, data) { throw new SchemeTrapError(tag, data); },
|
||||
wtf8_to_string(wtf8) { return wtf8_to_string(wtf8); },
|
||||
string_to_wtf8(str) { return string_to_wtf8(str); },
|
||||
|
@ -348,6 +512,11 @@ class SchemeTrapError extends Error {
|
|||
toString() { return `SchemeTrap(${this.tag}, <data>)`; }
|
||||
}
|
||||
|
||||
class SchemeQuitError extends Error {
|
||||
constructor(status) { super(); this.status = status; }
|
||||
toString() { return `SchemeQuit(status=${this.status})`; }
|
||||
}
|
||||
|
||||
function string_repr(str) {
|
||||
// FIXME: Improve to match Scheme.
|
||||
return '"' + str.replace(/(["\\])/g, '\\$1').replace(/\n/g, '\\n') + '"';
|
||||
|
@ -415,6 +584,18 @@ async function load_wtf8_helper_module(reflect_wasm_dir = '') {
|
|||
wtf8_helper = instance;
|
||||
}
|
||||
|
||||
function make_textual_writable_stream(write_chars) {
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
return new WritableStream({
|
||||
write(chunk) {
|
||||
return new Promise((resolve, reject) => {
|
||||
write_chars(decoder.decode(chunk, { stream: true }));
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class SchemeModule {
|
||||
#instance;
|
||||
#io_handler;
|
||||
|
@ -469,7 +650,6 @@ class SchemeModule {
|
|||
bignum_logand(a, b) { return BigInt(a) & BigInt(b); },
|
||||
bignum_logior(a, b) { return BigInt(a) | BigInt(b); },
|
||||
bignum_logxor(a, b) { return BigInt(a) ^ BigInt(b); },
|
||||
bignum_logsub(a, b) { return BigInt(a) & (~ BigInt(b)); },
|
||||
|
||||
bignum_lt(a, b) { return a < b; },
|
||||
bignum_le(a, b) { return a <= b; },
|
||||
|
@ -477,14 +657,17 @@ class SchemeModule {
|
|||
|
||||
bignum_to_f64(n) { return Number(n); },
|
||||
|
||||
f64_is_nan(n) { return Number.isNaN(n); },
|
||||
f64_is_infinite(n) { return !Number.isFinite(n); },
|
||||
|
||||
flonum_to_string,
|
||||
|
||||
string_upcase: Function.call.bind(String.prototype.toUpperCase),
|
||||
string_downcase: Function.call.bind(String.prototype.toLowerCase),
|
||||
|
||||
make_weak_ref(x) { return new WeakRef(x); },
|
||||
weak_ref_deref(ref, fail) {
|
||||
const val = ref.deref();
|
||||
return val === undefined ? fail: val;
|
||||
},
|
||||
|
||||
make_weak_map() { return new WeakMap; },
|
||||
weak_map_get(map, k, fail) {
|
||||
const val = map.get(k);
|
||||
|
@ -493,6 +676,14 @@ class SchemeModule {
|
|||
weak_map_set(map, k, v) { return map.set(k, v); },
|
||||
weak_map_delete(map, k) { return map.delete(k); },
|
||||
|
||||
make_finalization_registry(f) { return new FinalizationRegistry(f); },
|
||||
finalization_registry_register(registry, target, heldValue, unregisterToken) {
|
||||
registry.register(target, heldValue, unregisterToken);
|
||||
},
|
||||
finalization_registry_unregister(registry, unregisterToken) {
|
||||
registry.unregister(unregisterToken);
|
||||
},
|
||||
|
||||
fsqrt: Math.sqrt,
|
||||
fsin: Math.sin,
|
||||
fcos: Math.cos,
|
||||
|
@ -510,7 +701,15 @@ class SchemeModule {
|
|||
|
||||
async_invoke,
|
||||
async_invoke_later,
|
||||
promise_on_completed(p, kt, kf) { p.then(kt, kf); },
|
||||
promise_on_completed(p, kt, kf) {
|
||||
p.then((val) => {
|
||||
if (val === undefined) {
|
||||
kt(false);
|
||||
} else {
|
||||
kt(val);
|
||||
}
|
||||
}, kf);
|
||||
},
|
||||
promise_complete(callback, val) { callback(val); },
|
||||
|
||||
// Wrap in functions to allow for lazy loading of the wtf8
|
||||
|
@ -518,11 +717,73 @@ class SchemeModule {
|
|||
wtf8_to_string(wtf8) { return wtf8_to_string(wtf8); },
|
||||
string_to_wtf8(str) { return string_to_wtf8(str); },
|
||||
|
||||
die(tag, data) { throw new SchemeTrapError(tag, data); }
|
||||
make_regexp(pattern, flags) { return new RegExp(pattern, flags); },
|
||||
regexp_exec(re, str) { return re.exec(str); },
|
||||
regexp_match_string(m) { return m.input; },
|
||||
regexp_match_start(m) { return m.index; },
|
||||
regexp_match_end(m) { return m.index + m[0].length; },
|
||||
regexp_match_count(m) { return m.length; },
|
||||
regexp_match_substring(m, i) {
|
||||
const str = m[i];
|
||||
if (str === undefined) {
|
||||
return null;
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
die(tag, data) { throw new SchemeTrapError(tag, data); },
|
||||
quit(status) { throw new SchemeQuitError(status); },
|
||||
|
||||
stream_make_chunk(len) { return new Uint8Array(len); },
|
||||
stream_chunk_length(chunk) { return chunk.length; },
|
||||
stream_chunk_ref(chunk, idx) { return chunk[idx]; },
|
||||
stream_chunk_set(chunk, idx, val) { chunk[idx] = val; },
|
||||
stream_get_reader(stream) { return stream.getReader(); },
|
||||
stream_read(reader) { return reader.read(); },
|
||||
stream_result_chunk(result) { return result.value; },
|
||||
stream_result_done(result) { return result.done ? 1 : 0; },
|
||||
stream_get_writer(stream) { return stream.getWriter(); },
|
||||
stream_write(writer, chunk) { return writer.write(chunk); },
|
||||
stream_close_writer(writer) { return writer.close(); },
|
||||
};
|
||||
|
||||
static #code_origins = new WeakMap;
|
||||
static #all_modules = new IterableWeakSet;
|
||||
static #code_origin(code) {
|
||||
if (SchemeModule.#code_origins.has(code))
|
||||
return SchemeModule.#code_origins.get(code);
|
||||
for (let mod of SchemeModule.#all_modules) {
|
||||
for (let i = 0, x = null; x = mod.instance_code(i); i++) {
|
||||
let origin = [mod, i];
|
||||
if (!SchemeModule.#code_origins.has(x))
|
||||
SchemeModule.#code_origins.set(x, origin);
|
||||
if (x === code)
|
||||
return origin;
|
||||
}
|
||||
}
|
||||
return [null, 0];
|
||||
}
|
||||
|
||||
static #code_name(code) {
|
||||
let [mod, idx] = SchemeModule.#code_origin(code);
|
||||
if (mod)
|
||||
return mod.instance_code_name(idx);
|
||||
return null;
|
||||
}
|
||||
|
||||
static #code_source(code) {
|
||||
let [mod, idx] = SchemeModule.#code_origin(code);
|
||||
if (mod)
|
||||
return mod.instance_code_source(idx);
|
||||
return [null, 0, 0];
|
||||
}
|
||||
|
||||
constructor(instance) {
|
||||
SchemeModule.#all_modules.add(this);
|
||||
this.#instance = instance;
|
||||
let open_file_error = (filename) => {
|
||||
throw new Error('No file system access');
|
||||
};
|
||||
if (typeof printErr === 'function') { // v8/sm dev console
|
||||
// On the console, try to use 'write' (v8) or 'putstr' (sm),
|
||||
// as these don't add an extraneous newline. Unfortunately
|
||||
|
@ -541,14 +802,13 @@ class SchemeModule {
|
|||
return '\n';
|
||||
}
|
||||
}: () => '';
|
||||
let delete_file = (filename) => false;
|
||||
this.#io_handler = {
|
||||
write_stdout: write_no_newline,
|
||||
write_stderr: printErr,
|
||||
read_stdin,
|
||||
file_exists: (filename) => false,
|
||||
open_input_file: (filename) => {},
|
||||
open_output_file: (filename) => {},
|
||||
open_input_file: open_file_error,
|
||||
open_output_file: open_file_error,
|
||||
close_file: () => undefined,
|
||||
read_file: (handle, length) => 0,
|
||||
write_file: (handle, length) => 0,
|
||||
|
@ -557,7 +817,11 @@ class SchemeModule {
|
|||
file_buffer_size: (handle) => 0,
|
||||
file_buffer_ref: (handle, i) => 0,
|
||||
file_buffer_set: (handle, i, x) => undefined,
|
||||
delete_file: (filename) => undefined
|
||||
delete_file: (filename) => undefined,
|
||||
// FIXME: We should polyfill these out.
|
||||
stream_stdin() { throw new Error('stream_stdin not implemented'); },
|
||||
stream_stdout() { throw new Error('stream_stderr not implemented'); },
|
||||
stream_stderr() { throw new Error('stream_stderr not implemented'); },
|
||||
};
|
||||
} else if (typeof window !== 'undefined') { // web browser
|
||||
this.#io_handler = {
|
||||
|
@ -565,8 +829,8 @@ class SchemeModule {
|
|||
write_stderr: console.error,
|
||||
read_stdin: () => '',
|
||||
file_exists: (filename) => false,
|
||||
open_input_file: (filename) => {},
|
||||
open_output_file: (filename) => {},
|
||||
open_input_file: open_file_error,
|
||||
open_output_file: open_file_error,
|
||||
close_file: () => undefined,
|
||||
read_file: (handle, length) => 0,
|
||||
write_file: (handle, length) => 0,
|
||||
|
@ -575,17 +839,26 @@ class SchemeModule {
|
|||
file_buffer_size: (handle) => 0,
|
||||
file_buffer_ref: (handle, i) => 0,
|
||||
file_buffer_set: (handle, i, x) => undefined,
|
||||
delete_file: (filename) => undefined
|
||||
delete_file: (filename) => undefined,
|
||||
stream_stdin() { return new ReadableStream; },
|
||||
stream_stdout() {
|
||||
return make_textual_writable_stream(s => console.log(s));
|
||||
},
|
||||
stream_stderr() {
|
||||
return make_textual_writable_stream(s => console.error(s));
|
||||
},
|
||||
};
|
||||
} else { // nodejs
|
||||
const fs = require('fs');
|
||||
const process = require('process');
|
||||
const { ReadableStream, WritableStream } = require('node:stream/web');
|
||||
|
||||
const bufLength = 1024;
|
||||
const stdinBuf = Buffer.alloc(bufLength);
|
||||
const SEEK_SET = 0, SEEK_CUR = 1, SEEK_END = 2;
|
||||
this.#io_handler = {
|
||||
write_stdout: console.log,
|
||||
write_stderr: console.error,
|
||||
write_stdout: process.stdout.write.bind(process.stdout),
|
||||
write_stderr: process.stderr.write.bind(process.stderr),
|
||||
read_stdin: () => {
|
||||
let n = fs.readSync(process.stdin.fd, stdinBuf, 0, stdinBuf.length);
|
||||
return stdinBuf.toString('utf8', 0, n);
|
||||
|
@ -646,7 +919,23 @@ class SchemeModule {
|
|||
file_buffer_set: (handle, i, x) => {
|
||||
handle.buf[i] = x;
|
||||
},
|
||||
delete_file: fs.rmSync.bind(fs)
|
||||
delete_file: fs.rmSync.bind(fs),
|
||||
stream_stdin() {
|
||||
return new ReadableStream({
|
||||
async start(controller) {
|
||||
for await (const chunk of process.stdin) {
|
||||
controller.enqueue(chunk);
|
||||
}
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
},
|
||||
stream_stdout() {
|
||||
return make_textual_writable_stream(s => process.stdout.write(s));
|
||||
},
|
||||
stream_stderr() {
|
||||
return make_textual_writable_stream(s => process.stderr.write(s));
|
||||
},
|
||||
};
|
||||
}
|
||||
this.#debug_handler = {
|
||||
|
@ -673,12 +962,17 @@ class SchemeModule {
|
|||
file_buffer_size(handle) { return mod.#io_handler.file_buffer_size(handle); },
|
||||
file_buffer_ref(handle, i) { return mod.#io_handler.file_buffer_ref(handle, i); },
|
||||
file_buffer_set(handle, i, x) { return mod.#io_handler.file_buffer_set(handle, i, x); },
|
||||
delete_file(filename) { mod.#io_handler.delete_file(filename); }
|
||||
delete_file(filename) { mod.#io_handler.delete_file(filename); },
|
||||
stream_stdin() { return mod.#io_handler.stream_stdin(); },
|
||||
stream_stdout() { return mod.#io_handler.stream_stdout(); },
|
||||
stream_stderr() { return mod.#io_handler.stream_stderr(); },
|
||||
};
|
||||
let debug = {
|
||||
debug_str(x) { mod.#debug_handler.debug_str(x); },
|
||||
debug_str_i32(x, y) { mod.#debug_handler.debug_str_i32(x, y); },
|
||||
debug_str_scm(x, y) { mod.#debug_handler.debug_str_scm(x, y); },
|
||||
code_name(code) { return SchemeModule.#code_name(code); },
|
||||
code_source(code) { return SchemeModule.#code_source(code); },
|
||||
}
|
||||
let ffi = {
|
||||
procedure_to_extern(proc) {
|
||||
|
@ -718,6 +1012,24 @@ class SchemeModule {
|
|||
return this.all_exports()[name];
|
||||
throw new Error(`unknown export: ${name}`)
|
||||
}
|
||||
instance_code(idx) {
|
||||
if ('%instance-code' in this.all_exports()) {
|
||||
return this.all_exports()['%instance-code'](idx);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
instance_code_name(idx) {
|
||||
if ('%instance-code-name' in this.all_exports()) {
|
||||
return this.all_exports()['%instance-code-name'](idx);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
instance_code_source(idx) {
|
||||
if ('%instance-code-source' in this.all_exports()) {
|
||||
return this.all_exports()['%instance-code-source'](idx);
|
||||
}
|
||||
return [null, 0, 0];
|
||||
}
|
||||
async reflect(opts = {}) {
|
||||
return await Scheme.reflect(this.exported_abi(), opts);
|
||||
}
|
||||
|
@ -734,3 +1046,10 @@ function repr(obj) {
|
|||
return string_repr(obj);
|
||||
return obj + '';
|
||||
}
|
||||
|
||||
// Modulize when possible.
|
||||
if (typeof exports !== 'undefined') {
|
||||
exports.Scheme = Scheme;
|
||||
exports.SchemeQuitError = SchemeQuitError;
|
||||
exports.repr = repr;
|
||||
}
|
||||
|
|
BIN
reflect.wasm
BIN
reflect.wasm
Binary file not shown.
Loading…
Add table
Reference in a new issue