2019-03-10 10:37:01 +01:00
|
|
|
const isSafari =
|
|
|
|
typeof navigator != "undefined" &&
|
|
|
|
/^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2018-05-12 21:09:19 +02:00
|
|
|
class WAForth {
|
2019-03-10 14:32:41 +01:00
|
|
|
constructor(wasmModule, arrayToBase64) {
|
2019-03-10 10:37:01 +01:00
|
|
|
if (wasmModule == null) {
|
|
|
|
this.wasmModule = require("../waforth.wasm");
|
|
|
|
} else {
|
|
|
|
this.wasmModule = wasmModule;
|
|
|
|
}
|
2019-03-10 14:32:41 +01:00
|
|
|
this.arrayToBase64 =
|
|
|
|
arrayToBase64 ||
|
|
|
|
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);
|
|
|
|
};
|
2019-03-10 10:37:01 +01:00
|
|
|
}
|
|
|
|
|
2018-05-13 17:07:54 +02:00
|
|
|
start(options = {}) {
|
|
|
|
const { skipPrelude } = options;
|
2018-05-12 21:09:19 +02:00
|
|
|
let table;
|
2018-05-27 21:49:22 +02:00
|
|
|
let memory;
|
2018-06-03 15:17:38 +02:00
|
|
|
const buffer = (this.buffer = []);
|
2018-05-12 21:09:19 +02:00
|
|
|
|
2019-03-10 10:37:01 +01:00
|
|
|
return WebAssembly.instantiate(this.wasmModule, {
|
2018-05-29 13:06:24 +02:00
|
|
|
shell: {
|
|
|
|
////////////////////////////////////////
|
|
|
|
// I/O
|
|
|
|
////////////////////////////////////////
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2018-05-29 13:06:24 +02:00
|
|
|
emit: this.onEmit,
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2018-06-03 15:17:38 +02:00
|
|
|
key: () => {
|
|
|
|
if (buffer.length === 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return buffer.pop();
|
|
|
|
},
|
|
|
|
|
2018-05-29 13:06:24 +02:00
|
|
|
debug: d => {
|
|
|
|
console.log("DEBUG: ", d);
|
|
|
|
},
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2018-05-29 13:06:24 +02:00
|
|
|
////////////////////////////////////////
|
|
|
|
// Loader
|
|
|
|
////////////////////////////////////////
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2018-05-29 13:06:24 +02:00
|
|
|
load: (offset, length, index) => {
|
|
|
|
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]);
|
2018-05-12 21:09:19 +02:00
|
|
|
}
|
2018-05-29 13:06:24 +02:00
|
|
|
data = new Uint8Array(dataCopy);
|
|
|
|
}
|
|
|
|
if (index >= table.length) {
|
|
|
|
table.grow(table.length); // Double size
|
2018-05-12 21:09:19 +02:00
|
|
|
}
|
2019-03-10 14:32:41 +01:00
|
|
|
// console.log("Load", index, this.arrayToBase64(data));
|
2018-05-29 13:06:24 +02:00
|
|
|
var module = new WebAssembly.Module(data);
|
|
|
|
new WebAssembly.Instance(module, {
|
2019-03-08 21:52:14 +01:00
|
|
|
env: { table, memory, tos: -1 }
|
2018-05-29 13:06:24 +02:00
|
|
|
});
|
2018-05-13 17:07:54 +02:00
|
|
|
}
|
2018-05-29 13:06:24 +02:00
|
|
|
}
|
|
|
|
}).then(instance => {
|
|
|
|
this.core = instance.instance;
|
|
|
|
table = this.core.exports.table;
|
|
|
|
memory = this.core.exports.memory;
|
|
|
|
if (!skipPrelude) {
|
|
|
|
this.core.exports.loadPrelude();
|
|
|
|
}
|
|
|
|
});
|
2018-05-12 21:09:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
read(s) {
|
|
|
|
const data = new TextEncoder().encode(s);
|
2018-06-03 15:17:38 +02:00
|
|
|
for (let i = data.length - 1; i >= 0; --i) {
|
|
|
|
this.buffer.push(data[i]);
|
2018-05-12 21:09:19 +02:00
|
|
|
}
|
|
|
|
}
|
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;
|