2018-05-12 21:09:19 +02:00
|
|
|
let wasmModule;
|
|
|
|
|
2018-05-13 17:07:54 +02:00
|
|
|
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
|
|
|
2018-05-12 21:09:19 +02:00
|
|
|
// eslint-disable-next-line no-unused-vars
|
|
|
|
function arrayToBase64(bytes) {
|
|
|
|
var binary = "";
|
|
|
|
var len = bytes.byteLength;
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
|
|
binary += String.fromCharCode(bytes[i]);
|
|
|
|
}
|
|
|
|
return window.btoa(binary);
|
|
|
|
}
|
|
|
|
|
|
|
|
class WAForth {
|
2018-05-13 17:07:54 +02:00
|
|
|
start(options = {}) {
|
|
|
|
const { skipPrelude } = options;
|
2018-05-12 21:09:19 +02:00
|
|
|
let table;
|
|
|
|
const buffer = (this.buffer = []);
|
|
|
|
|
|
|
|
// TODO: Try to bundle this. See https://github.com/parcel-bundler/parcel/issues/647
|
|
|
|
const initialize =
|
|
|
|
wasmModule != null
|
|
|
|
? Promise.resolve(wasmModule)
|
|
|
|
: fetch("waforth.wasm")
|
|
|
|
.then(resp => resp.arrayBuffer())
|
|
|
|
.then(module => {
|
|
|
|
wasmModule = module;
|
|
|
|
return wasmModule;
|
|
|
|
});
|
|
|
|
return initialize
|
|
|
|
.then(m =>
|
|
|
|
WebAssembly.instantiate(m, {
|
|
|
|
shell: {
|
2018-05-13 17:07:54 +02:00
|
|
|
////////////////////////////////////////
|
|
|
|
// I/O
|
|
|
|
////////////////////////////////////////
|
|
|
|
|
2018-05-12 21:09:19 +02:00
|
|
|
emit: this.onEmit,
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2018-05-12 21:09:19 +02:00
|
|
|
key: () => {
|
|
|
|
if (buffer.length === 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return buffer.pop();
|
|
|
|
},
|
2018-05-13 17:07:54 +02:00
|
|
|
|
|
|
|
debug: d => {
|
|
|
|
console.log("DEBUG: ", d);
|
|
|
|
},
|
|
|
|
|
|
|
|
////////////////////////////////////////
|
|
|
|
// Loader
|
|
|
|
////////////////////////////////////////
|
|
|
|
|
2018-05-27 21:23:20 +02:00
|
|
|
load: (offset, length, index) => {
|
2018-05-13 17:07:54 +02:00
|
|
|
let data = new Uint8Array(
|
|
|
|
this.core.exports.memory.buffer,
|
|
|
|
offset,
|
|
|
|
length
|
|
|
|
);
|
|
|
|
if (isSafari) {
|
|
|
|
// On Safari, using the original Uint8Array triggers a bug.
|
|
|
|
// Taking an element-by-element copy of the data first.
|
|
|
|
let dataCopy = [];
|
|
|
|
for (let i = 0; i < length; ++i) {
|
|
|
|
dataCopy.push(data[i]);
|
|
|
|
}
|
|
|
|
data = new Uint8Array(dataCopy);
|
|
|
|
}
|
2018-05-27 21:23:20 +02:00
|
|
|
if (index >= table.length) {
|
2018-05-12 21:09:19 +02:00
|
|
|
table.grow(table.length); // Double size
|
|
|
|
}
|
2018-05-19 14:17:16 +02:00
|
|
|
// console.log(
|
|
|
|
// "Load",
|
2018-05-27 21:23:20 +02:00
|
|
|
// index,
|
2018-05-19 14:17:16 +02:00
|
|
|
// new Uint8Array(data),
|
|
|
|
// arrayToBase64(data)
|
|
|
|
// );
|
2018-05-27 16:40:47 +02:00
|
|
|
var module = new WebAssembly.Module(data);
|
2018-05-12 21:09:19 +02:00
|
|
|
new WebAssembly.Instance(module, {
|
2018-05-27 21:23:20 +02:00
|
|
|
env: { table, tableBase: index }
|
2018-05-12 21:09:19 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.then(instance => {
|
|
|
|
this.core = instance.instance;
|
|
|
|
table = this.core.exports.table;
|
2018-05-13 17:07:54 +02:00
|
|
|
if (!skipPrelude) {
|
|
|
|
this.core.exports.loadPrelude();
|
|
|
|
}
|
2018-05-12 21:09:19 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
read(s) {
|
|
|
|
const data = new TextEncoder().encode(s);
|
|
|
|
for (let i = data.length - 1; i >= 0; --i) {
|
|
|
|
this.buffer.push(data[i]);
|
|
|
|
}
|
|
|
|
}
|
2018-05-13 17:07:54 +02:00
|
|
|
|
|
|
|
run(s) {
|
|
|
|
this.read(s);
|
|
|
|
return this.core.exports.interpret();
|
|
|
|
}
|
2018-05-12 21:09:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export default WAForth;
|