315 lines
7.7 KiB
JavaScript
315 lines
7.7 KiB
JavaScript
|
|
const TYPES = {
|
|
U8: 1,
|
|
I8: 2,
|
|
U16: 3,
|
|
I16: 4,
|
|
U32: 5,
|
|
I32: 6,
|
|
U64: 7,
|
|
I64: 8,
|
|
SizeT: 9,
|
|
F32: 10,
|
|
F64: 11,
|
|
Bool: 12,
|
|
String: 13,
|
|
U8Array: 14,
|
|
I8Array: 15,
|
|
U16Array: 16,
|
|
I16Array: 17,
|
|
U32Array: 18,
|
|
I32Array: 19,
|
|
U64Array: 20,
|
|
I64Array: 21,
|
|
F32Array: 22,
|
|
F64Array: 23,
|
|
Char: 24,
|
|
CharArray: 25,
|
|
};
|
|
|
|
let LoadSize = null;
|
|
|
|
function assert(condition, message)
|
|
{
|
|
if(!condition)
|
|
{
|
|
console.assert(condition, message);
|
|
throw "Assert failure";
|
|
}
|
|
}
|
|
|
|
const wasm_manager = {
|
|
memory: null,
|
|
data_view: null,
|
|
stdout_string: "",
|
|
sprintf_values: [],
|
|
ptr_size: 4,
|
|
};
|
|
|
|
function InitWasmManager(memory, ptr_size = 4)
|
|
{
|
|
wasm_manager.ptr_size = ptr_size;
|
|
|
|
wasm_manager.sprintf_values = [];
|
|
|
|
wasm_manager.stdout_string = "";
|
|
|
|
switch(wasm_manager.ptr_size)
|
|
{
|
|
case 8: LoadSize = LoadU64; break;
|
|
case 4:
|
|
default: LoadSize = LoadU32; break;
|
|
}
|
|
}
|
|
|
|
function SetMemory(memory)
|
|
{
|
|
wasm_manager.memory = memory;
|
|
wasm_manager.data_view = new DataView(wasm_manager.memory.buffer);
|
|
}
|
|
|
|
function Memory()
|
|
{
|
|
return new DataView(wasm_manager.memory.buffer);
|
|
}
|
|
|
|
function LoadBool(addr) { return Boolean(Memory().getUint8(addr, true)); }
|
|
function LoadF32(addr) { return Memory().getFloat32(addr, true); }
|
|
function LoadF64(addr) { return Memory().getFloat64(addr, true); }
|
|
function LoadU8 (addr) { return Memory().getUint8 (addr, true); }
|
|
function LoadI8 (addr) { return Memory().getInt8 (addr, true); }
|
|
function LoadU16(addr) { return Memory().getUint16 (addr, true); }
|
|
function LoadI16(addr) { return Memory().getInt16 (addr, true); }
|
|
function LoadU32(addr) { return Memory().getUint32 (addr, true); }
|
|
function LoadI32(addr) { return Memory().getInt32 (addr, true); }
|
|
|
|
function LoadU64(addr)
|
|
{
|
|
const lo = Memory().getUint32(addr+0, true);
|
|
const hi = Memory().getUint32(addr+4, true);
|
|
return lo + hi*4294967296;
|
|
}
|
|
|
|
function LoadI64(addr)
|
|
{
|
|
const lo = Memory().getUint32(addr+0, true);
|
|
const hi = Memory().getInt32 (addr+4, true);
|
|
return lo + hi*4294967296;
|
|
}
|
|
|
|
function LoadArray(array_class, length, addr)
|
|
{
|
|
return new array_class(wasm_manager.memory.buffer, addr, length);
|
|
}
|
|
|
|
function LoadString(length, addr)
|
|
{
|
|
return new TextDecoder().decode(LoadArray(Uint8Array, length, addr));
|
|
}
|
|
|
|
function LoadCString(ptr)
|
|
{
|
|
if(!ptr) return null;
|
|
|
|
let length = 0;
|
|
for(; LoadU8(ptr+length); length += 1) {}
|
|
|
|
return LoadString(ptr, length);
|
|
}
|
|
|
|
function StoreString(ptr, value)
|
|
{
|
|
const src = new TextEncoder().encode(value);
|
|
const dst = new Uint8Array(wasm_manager.memory.buffer, ptr, src.length);
|
|
|
|
dst.set(src);
|
|
|
|
return src.length;
|
|
}
|
|
|
|
const imports = {
|
|
env: {
|
|
Abort(string_length, string_ptr)
|
|
{
|
|
console.error(LoadString(string_length, string_ptr));
|
|
},
|
|
SprintfLoadValue(ptr, type)
|
|
{
|
|
let value = null;
|
|
switch(type)
|
|
{
|
|
case TYPES.U8: value = LoadU8(ptr); break;
|
|
case TYPES.I8: value = LoadI8(ptr); break;
|
|
case TYPES.U16: value = LoadU16(ptr); break;
|
|
case TYPES.I16: value = LoadI16(ptr); break;
|
|
case TYPES.U32: value = LoadU32(ptr); break;
|
|
case TYPES.I32: value = LoadI32(ptr); break;
|
|
case TYPES.U64: value = LoadU64(ptr); break;
|
|
case TYPES.I64: value = LoadI64(ptr); break;
|
|
case TYPES.SizeT: value = LoadSize(ptr); break;
|
|
case TYPES.F32: value = LoadF32(ptr); break;
|
|
case TYPES.F64: value = LoadF64(ptr); break;
|
|
case TYPES.Bool: value = LoadBool(ptr); break;
|
|
case TYPES.Char: value = LoadString(1, ptr); break;
|
|
default: break;
|
|
}
|
|
|
|
if(value != null)
|
|
{
|
|
if(type === TYPES.Bool)
|
|
{
|
|
wasm_manager.sprintf_values.push(value ? 'true' : 'false');
|
|
}
|
|
else
|
|
{
|
|
wasm_manager.sprintf_values.push(''+value);
|
|
}
|
|
}
|
|
},
|
|
SprintfLoadArray(length, ptr, type)
|
|
{
|
|
let value = null;
|
|
switch(type)
|
|
{
|
|
case TYPES.CharArray:
|
|
case TYPES.String: value = LoadString(length, ptr); break;
|
|
case TYPES.U8Array: value = LoadArray(Uint8Array, length, ptr); break;
|
|
case TYPES.I8Array: value = LoadArray(Int8Array, length, ptr); break;
|
|
case TYPES.U16Array: value = LoadArray(Uint16Array, length, ptr); break;
|
|
case TYPES.I16Array: value = LoadArray(Int16Array, length, ptr); break;
|
|
case TYPES.U32Array: value = LoadArray(Uint32Array, length, ptr); break;
|
|
case TYPES.I32Array: value = LoadArray(Int32Array, length, ptr); break;
|
|
case TYPES.U64Array: value = LoadArray(BigUint64Array, length, ptr); break;
|
|
case TYPES.I64Array: value = LoadArray(BigInt64Array, length, ptr); break;
|
|
case TYPES.F32Array: value = LoadArray(Float32Array, length, ptr); break;
|
|
case TYPES.F64Array: value = LoadArray(Float64Array, length, ptr); break;
|
|
}
|
|
|
|
if(value)
|
|
{
|
|
if(type !== TYPES.String && type !== TYPES.CharArray)
|
|
{
|
|
value = `[${value.join(", ")}]`;
|
|
}
|
|
|
|
wasm_manager.sprintf_values.push(value);
|
|
}
|
|
},
|
|
SprintfEnd(buffer_length, buffer_ptr, string_length, string_ptr)
|
|
{
|
|
let format_string = LoadString(string_length, string_ptr);
|
|
|
|
let index = format_string.indexOf("%");
|
|
|
|
let result = format_string.substring(0, index);
|
|
let format = format_string.substring(index);
|
|
|
|
function Next()
|
|
{
|
|
index = format.indexOf("%");
|
|
result += format.substring(0, index);
|
|
format = format.substring(index);
|
|
}
|
|
|
|
let value_index = 0;
|
|
for(; value_index < wasm_manager.sprintf_values.length;)
|
|
{
|
|
assert(format.length);
|
|
|
|
const char = format.charAt(1);
|
|
assert(char !== " " && char.length, "Invalid format string " + format_string);
|
|
|
|
if(char === "%")
|
|
{
|
|
result += "%";
|
|
format = format.substring(2);
|
|
Next();
|
|
continue;
|
|
}
|
|
else if(char >= '0' && char <= '9')
|
|
{
|
|
let digit_index = 2;
|
|
for(;;)
|
|
{
|
|
let next_char = format.charAt(digit_index++);
|
|
|
|
assert(next_char !== " " && next_char.length, "Invalid format string " + format_string);
|
|
|
|
if(next_char < '0' || next_char > '9')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
result += wasm_manager.sprintf_values[value_index++];
|
|
format = format.substring(digit_index);
|
|
Next();
|
|
|
|
break;
|
|
}
|
|
}
|
|
else if((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z'))
|
|
{
|
|
result += wasm_manager.sprintf_values[value_index++];
|
|
format = format.substring(2);
|
|
Next();
|
|
}
|
|
else assert(false, "Invalid format string " + format_string);
|
|
}
|
|
|
|
result += format;
|
|
|
|
const bytes_written = StoreString(buffer_ptr, result);
|
|
assert(bytes_written <= buffer_length, "Format string is longer than buffer length");
|
|
|
|
wasm_manager.sprintf_values = [];
|
|
|
|
return bytes_written;
|
|
},
|
|
Console(string_length, string_ptr, write_line)
|
|
{
|
|
wasm_manager.stdout_string += LoadString(string_length, string_ptr);
|
|
const arr = LoadArray(Uint8Array, string_length, string_ptr);
|
|
|
|
if(write_line || wasm_manager.stdout_string.includes("\n"))
|
|
{
|
|
console.log(wasm_manager.stdout_string);
|
|
wasm_manager.stdout_string = "";
|
|
}
|
|
},
|
|
Console2(string_length, string_ptr, write_line)
|
|
{
|
|
wasm_manager.stdout_string += LoadString(string_length, string_ptr);
|
|
const arr = LoadArray(Uint8Array, string_length, string_ptr);
|
|
|
|
if(write_line || wasm_manager.stdout_string.includes("\n"))
|
|
{
|
|
console.log(wasm_manager.stdout_string);
|
|
wasm_manager.stdout_string = "";
|
|
}
|
|
},
|
|
pow: Math.pow,
|
|
cos: Math.cos,
|
|
acos: Math.acos,
|
|
},
|
|
}
|
|
|
|
async function StartWasm(path)
|
|
{
|
|
const response = await fetch(path);
|
|
const data = await response.arrayBuffer();
|
|
const wasm = await WebAssembly.instantiate(data, imports);
|
|
|
|
const exports = wasm.instance.exports;
|
|
|
|
if(exports.memory)
|
|
{
|
|
SetMemory(exports.memory);
|
|
}
|
|
|
|
exports?._start();
|
|
|
|
exports?.RunTests();
|
|
|
|
return;
|
|
}
|