mirror of
https://github.com/fluencelabs/wasm-utils
synced 2025-03-27 16:31:05 +00:00
293 lines
10 KiB
HTML
293 lines
10 KiB
HTML
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<script>
|
|
// Check for wasm support.
|
|
if (!('WebAssembly' in window)) {
|
|
alert('you need a browser with wasm support enabled :(');
|
|
}
|
|
|
|
if (!ArrayBuffer.transfer) {
|
|
ArrayBuffer.transfer = function(source, length) {
|
|
source = Object(source);
|
|
var dest = new ArrayBuffer(length);
|
|
if (!(source instanceof ArrayBuffer) || !(dest instanceof ArrayBuffer)) {
|
|
throw new TypeError('Source and destination must be ArrayBuffer instances');
|
|
}
|
|
if (dest.byteLength >= source.byteLength) {
|
|
var nextOffset = 0;
|
|
var leftBytes = source.byteLength;
|
|
var wordSizes = [8, 4, 2, 1];
|
|
wordSizes.forEach(function(_wordSize_) {
|
|
if (leftBytes >= _wordSize_) {
|
|
var done = transferWith(_wordSize_, source, dest, nextOffset, leftBytes);
|
|
nextOffset = done.nextOffset;
|
|
leftBytes = done.leftBytes;
|
|
}
|
|
});
|
|
}
|
|
return dest;
|
|
function transferWith(wordSize, source, dest, nextOffset, leftBytes) {
|
|
var ViewClass = Uint8Array;
|
|
switch (wordSize) {
|
|
case 8:
|
|
ViewClass = Float64Array;
|
|
break;
|
|
case 4:
|
|
ViewClass = Float32Array;
|
|
break;
|
|
case 2:
|
|
ViewClass = Uint16Array;
|
|
break;
|
|
case 1:
|
|
ViewClass = Uint8Array;
|
|
break;
|
|
default:
|
|
ViewClass = Uint8Array;
|
|
break;
|
|
}
|
|
var view_source = new ViewClass(source, nextOffset, Math.trunc(leftBytes / wordSize));
|
|
var view_dest = new ViewClass(dest, nextOffset, Math.trunc(leftBytes / wordSize));
|
|
for (var i = 0; i < view_dest.length; i++) {
|
|
view_dest[i] = view_source[i];
|
|
}
|
|
return {
|
|
nextOffset : view_source.byteOffset + view_source.byteLength,
|
|
leftBytes : source.byteLength - (view_source.byteOffset + view_source.byteLength)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function Storage(memoryBuf) {
|
|
var self = this;
|
|
self.size = 16 * 1024;
|
|
self.buffer = new ArrayBuffer(self.size);
|
|
self.memory = memoryBuf;
|
|
self.total = 0;
|
|
|
|
self.write = function(offset, len, ptr) {
|
|
console.log("storage_write: " + JSON.stringify(arguments));
|
|
var oldSize = false;
|
|
while (offset + len > self.size) {
|
|
oldSize || (oldSize = self.size);
|
|
self.size = self.size * 2;
|
|
}
|
|
if (oldSize) {
|
|
self.buffer = ArrayBuffer.transfer(self.buffer, self.size);
|
|
}
|
|
|
|
if (offset + len > self.total) {
|
|
self.total = offset + len;
|
|
}
|
|
|
|
let memView = new DataView(self.memory);
|
|
let storageView = new DataView(self.buffer);
|
|
for (i = 0; i < len; i++) {
|
|
storageView.setInt8(offset+i, memView.getInt8(ptr+i));
|
|
}
|
|
return len;
|
|
}
|
|
|
|
self.read = function(offset, len, ptr) {
|
|
if (offset + len > self.total) {
|
|
return -1;
|
|
}
|
|
|
|
let memView = new DataView(self.memory);
|
|
let storageView = new DataView(self.buffer);
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
memView.setInt8(ptr+i, storageView.getInt8(offset+i));
|
|
}
|
|
return len;
|
|
}
|
|
|
|
self.size = function() {
|
|
return self.total;
|
|
}
|
|
|
|
self.toArr = function() {
|
|
let result = [];
|
|
let dataView = new DataView(self.buffer);
|
|
for (var i = 0; i < self.total; i++) {
|
|
result.push(dataView.getInt8(i));
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// function set_ptr(view, offset, val) {
|
|
// console.log("set_ptr: " + JSON.stringify(arguments));
|
|
|
|
// view[offset] = val & 0x000000ff;
|
|
// view[offset+1] = (val & 0x0000ff00) >> 8;
|
|
// view[offset+2] = (val & 0x00ff0000) >> 16;
|
|
// view[offset+3] = (val & 0xff000000) >> 24;
|
|
// }
|
|
|
|
// function get_ptr(view, offset) {
|
|
// return view[offset] + (view[offset] << 8) + (view[offset] << 16) + (view[offset] << 24);
|
|
// }
|
|
|
|
function Runtime() {
|
|
var self = this;
|
|
|
|
self.memory = new WebAssembly.Memory({ initial: 256, maximum: 256 });
|
|
self.storage = new Storage(self.memory.buffer);
|
|
|
|
self.resolveAlloc = function(instance) {
|
|
return instance.exports._malloc;
|
|
}
|
|
|
|
self.call = function(instance, args) {
|
|
console.log("call: " + JSON.stringify(args));
|
|
|
|
let alloc = self.resolveAlloc(instance);
|
|
// call descriptor size
|
|
let ptr = alloc(16);
|
|
let arg_ptr = alloc(args.length);
|
|
|
|
console.log("call allocs: " + JSON.stringify([ptr, arg_ptr]));
|
|
|
|
let dataView = new DataView(self.memory.buffer);
|
|
dataView.setInt32(ptr, arg_ptr, true);
|
|
dataView.setInt32(ptr+4, args.length, true);
|
|
|
|
for (var i = 0; i < args.length; i++) {
|
|
dataView.setInt8(arg_ptr+i, args[i], false);
|
|
}
|
|
|
|
debugger;
|
|
|
|
instance.exports._call(ptr);
|
|
|
|
let result_ptr = dataView.getInt32(ptr+8, true);
|
|
let result_length = dataView.getInt32(ptr+12, true);
|
|
|
|
console.log("result: " + JSON.stringify([result_ptr, result_length]));
|
|
|
|
let result = [];
|
|
if (result_ptr != 0) {
|
|
for (var i = 0; i < result_length; i++) {
|
|
result.push(dataView.getInt8(result_ptr + i));
|
|
}
|
|
}
|
|
return result;
|
|
|
|
}
|
|
}
|
|
|
|
function loadWebAssembly(filename, imports) {
|
|
return fetch(filename)
|
|
.then(response => response.arrayBuffer())
|
|
.then(buffer => WebAssembly.compile(buffer))
|
|
.then(module => {
|
|
imports = imports || {};
|
|
imports.env = imports.env || {};
|
|
var env = imports.env;
|
|
imports.env.memoryBase = imports.env.memoryBase || 1024;
|
|
imports.env.tableBase = imports.env.tableBase || 0;
|
|
|
|
window.runtime = new Runtime();
|
|
|
|
env.STACKTOP = env.STACKTOP || 0;
|
|
env.STACK_MAX = env.STACK_MAX || 5*1024*1024;
|
|
env.DYNAMICTOP_PTR = env.STACK_MAX;
|
|
env.enlargeMemory = env.enlargeMemory || function() {
|
|
console.log("called enlargeMemory(" + JSON.stringify(arguments) + ");");
|
|
return 1;
|
|
};
|
|
env.getTotalMemory = env.getTotalMemory || function() {
|
|
return 16 * 1024 * 1024;
|
|
};
|
|
env.abortOnCannotGrowMemory = env.abortOnCannotGrowMemory || function() { throw "abort growing memory"; };
|
|
env._abort = env._abort || function() { throw "_abort"; };
|
|
env.abort = env.abort || function() { throw "abort"; };
|
|
env.___setErrNo = env.___setErrNo || function() { throw "setting error no"; };
|
|
|
|
// dead symbols in rust wasm32-unknown-emscripten target
|
|
// todo: strip/raise issue in rust compiler
|
|
env.invoke_vi = function() { throw "invoke_vi: unreachable!"; }
|
|
env.invoke_v = function() { throw "invoke_v: unreachable!"; }
|
|
|
|
// todo: also test unwind about those two
|
|
env._rust_begin_unwind = function() { throw "_rust_begin_unwind: unreachable!"; }
|
|
env._llvm_trap = function() { throw "_llvm_trap: unreachable!"; }
|
|
|
|
env._emscripten_memcpy_big = function() { throw "_emscripten_memcpy_big: unreachable!"; }
|
|
env.___gxx_personality_v0 = function() { throw "___gxx_personality_v0: unreachable!"; }
|
|
env.___resumeException = function() { throw "___resumeException: unreachable!"; }
|
|
env.___cxa_find_matching_catch_2 = function() { throw "___cxa_find_matching_catch_2: unreachable!"; }
|
|
env.___gxx_personality_v0 = function() { throw "___gxx_personality_v0: unreachable!"; }
|
|
|
|
env.memoryBase = env.memoryBase || 0;
|
|
env.tableBase = env.tableBase || 0;
|
|
|
|
env._storage_read = runtime.storage.read;
|
|
env._storage_write = runtime.storage.write;
|
|
env._storage_size = runtime.storage.size;
|
|
|
|
env.gas = function(upd) {
|
|
console.log("used " + upd + " gas");
|
|
}
|
|
if (!imports.env.memory) {
|
|
imports.env.memory = runtime.memory;
|
|
}
|
|
if (!imports.env.table) {
|
|
imports.env.table = new WebAssembly.Table({ initial: 6, maximum: 6, element: 'anyfunc' });
|
|
}
|
|
return new WebAssembly.Instance(module, imports);
|
|
});
|
|
}
|
|
|
|
function strToArray(str) {
|
|
var src = str.trim().substr(1, str.length-2);
|
|
if (src.length == 0) {
|
|
return [];
|
|
} else {
|
|
return src.split(",").map(p => Number(p.trim()));
|
|
}
|
|
}
|
|
|
|
function arrayToStr(arr) {
|
|
return "[" + arr.join(", ") + "]";
|
|
}
|
|
|
|
loadWebAssembly('out/contract.wasm')
|
|
.then(instance => {
|
|
var button = document.getElementById('do-call');
|
|
button.value = 'Execute call';
|
|
button.addEventListener('click', function() {
|
|
let args = strToArray(document.getElementById("context").value);
|
|
let result = runtime.call(instance, args);
|
|
document.getElementById("result").value = arrayToStr(result);
|
|
document.getElementById("storage").value = arrayToStr(runtime.storage.toArr());
|
|
console.log("Call succeded");
|
|
}, false);
|
|
}
|
|
);
|
|
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div>
|
|
<label for="context" style="display: block">Context</label>
|
|
<textarea style="width: 480px; height: 96px; margin-bottom: 24px; resize: none" id="context">[]</textarea>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="storage" style="display: block">Storage</label>
|
|
<textarea style="width: 480px; height: 120px; margin-bottom: 24px; resize: none" id="storage">[]</textarea>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="result" style="display: block">Result</label>
|
|
<textarea style="width: 480px; height: 96px; margin-bottom: 24px; resize: none" id="result">[]</textarea>
|
|
</div>
|
|
|
|
<div style="height: 5px; border-top: 1px solid black"></div>
|
|
|
|
<input type="button" id="do-call" value="(waiting for WebAssembly)"></input>
|
|
</body>
|
|
</html> |