mirror of
https://github.com/fluencelabs/wasm-utils
synced 2025-06-27 05:21:38 +00:00
partly working
This commit is contained in:
@ -6,6 +6,158 @@
|
||||
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) {
|
||||
var oldSize = false;
|
||||
while (offset + len > self.size) {
|
||||
oldSize || (oldSize = self.size);
|
||||
self.size = self.size * 2;
|
||||
}
|
||||
if (oldSize != self.size) {
|
||||
self.buffer = ArrayBuffer.transfer(self.buffer, self.size);
|
||||
}
|
||||
|
||||
if (offset + len > self.total) {
|
||||
self.total = offset + len;
|
||||
}
|
||||
|
||||
let memView = new Int8Array(self.memory);
|
||||
let storageView = new Int8Array(self.buffer);
|
||||
for (i = 0; i < len; i++) {
|
||||
storageView[offset+i] = memView[ptr+i];
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
self.read = function(offset, len, ptr) {
|
||||
if (offset + len > self.total) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let memView = new Int8Array(self.memory);
|
||||
let storageView = new Int8Array(self.buffer);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
memView[ptr+i] = storageView[offset+i];
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
self.size = function() {
|
||||
return self.total;
|
||||
}
|
||||
}
|
||||
|
||||
function set_ptr(view, offset, val) {
|
||||
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) {
|
||||
let alloc = self.resolveAlloc(instance);
|
||||
// call descriptor size
|
||||
let ptr = alloc(16);
|
||||
let arg_ptr = alloc(args.length);
|
||||
let memView = new Int8Array(self.memory);
|
||||
set_ptr(memView, 0, args.length);
|
||||
set_ptr(memView, 4, arg_ptr);
|
||||
set_ptr(memView, 8, 0);
|
||||
set_ptr(memView, 12, 0);
|
||||
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
memView[arg_ptr+i] = args[i];
|
||||
}
|
||||
|
||||
instance.exports._call(ptr);
|
||||
|
||||
let result_length = get_ptr(memView, 8);
|
||||
let result_ptr = get_ptr(memView, 12);
|
||||
|
||||
let result = [];
|
||||
if (result_ptr != 0) {
|
||||
for (var i = 0; i < result_length; i++) {
|
||||
result.push(memVew[result_ptr + i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function loadWebAssembly(filename, imports) {
|
||||
return fetch(filename)
|
||||
@ -18,33 +170,7 @@
|
||||
imports.env.memoryBase = imports.env.memoryBase || 1024;
|
||||
imports.env.tableBase = imports.env.tableBase || 0;
|
||||
|
||||
/* rust/emscripten imports
|
||||
(import "env" "DYNAMICTOP_PTR" (global (;0;) i32))
|
||||
(import "env" "STACKTOP" (global (;1;) i32))
|
||||
(import "env" "STACK_MAX" (global (;2;) i32))
|
||||
(import "env" "enlargeMemory" (func (;0;) (type 0)))
|
||||
(import "env" "getTotalMemory" (func (;1;) (type 0)))
|
||||
(import "env" "abortOnCannotGrowMemory" (func (;2;) (type 0)))
|
||||
(import "env" "_abort" (func (;3;) (type 1)))
|
||||
(import "env" "___setErrNo" (func (;4;) (type 2)))
|
||||
|
||||
(import "env" "invoke_vi" (func (;4;) (type 3)))
|
||||
(import "env" "invoke_v" (func (;5;) (type 1)))
|
||||
(import "env" "_rust_begin_unwind" (func (;6;) (type 4)))
|
||||
(import "env" "_emscripten_memcpy_big" (func (;9;) (type 5)))
|
||||
(import "env" "___gxx_personality_v0" (func (;10;) (type 6)))
|
||||
(import "env" "_llvm_trap" (func (;11;) (type 0)))
|
||||
(import "env" "___resumeException" (func (;12;) (type 1)))
|
||||
(import "env" "___cxa_find_matching_catch_2" (func (;13;) (type 2)))
|
||||
(import "env" "___gxx_personality_v0" (func (;14;) (type 7)))
|
||||
|
||||
(import "env" "memory" (memory (;0;) 256 256))
|
||||
(import "env" "table" (table (;0;) 0 0 anyfunc))
|
||||
(import "env" "memoryBase" (global (;3;) i32))
|
||||
(import "env" "tableBase" (global (;4;) i32))
|
||||
*/
|
||||
|
||||
var memory = new WebAssembly.Memory({ initial: 256, maximum: 256 });
|
||||
window.runtime = new Runtime();
|
||||
|
||||
env.STACKTOP = env.STACKTOP || 0;
|
||||
env.STACK_MAX = env.STACK_MAX || 5*1024*1024;
|
||||
@ -79,40 +205,30 @@
|
||||
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 = memory;
|
||||
imports.env.memory = runtime.memory;
|
||||
}
|
||||
if (!imports.env.table) {
|
||||
imports.env.table = new WebAssembly.Table({ initial: 0, maximum: 0, element: 'anyfunc' });
|
||||
imports.env.table = new WebAssembly.Table({ initial: 6, maximum: 6, element: 'anyfunc' });
|
||||
}
|
||||
window.testMemory = memory;
|
||||
return new WebAssembly.Instance(module, imports);
|
||||
});
|
||||
}
|
||||
|
||||
loadWebAssembly('out/contract.wasm')
|
||||
.then(instance => {
|
||||
var exports = instance.exports;
|
||||
var call = exports._call;
|
||||
var malloc = exports._malloc;
|
||||
|
||||
// raw call context
|
||||
var context_ptr = malloc(256);
|
||||
|
||||
// raw persistent storage
|
||||
var storage_ptr = malloc(8192);
|
||||
|
||||
console.log("context_ptr: " + context_ptr);
|
||||
console.log("storage_ptr: " + storage_ptr);
|
||||
|
||||
var button = document.getElementById('do-call');
|
||||
button.value = 'Execute call';
|
||||
button.addEventListener('click', function() {
|
||||
let new_storage_ptr = call(context_ptr, storage_ptr);
|
||||
console.log("Call succeded");
|
||||
runtime.call(instance, []);
|
||||
console.log("Call succeded");
|
||||
}, false);
|
||||
}
|
||||
);
|
||||
|
@ -10,11 +10,53 @@ use std::slice;
|
||||
#[link_args = "-s WASM=1 -s NO_EXIT_RUNTIME=1 -s NO_FILESYSTEM=1 -s"]
|
||||
extern {}
|
||||
|
||||
mod storage {
|
||||
pub struct Error;
|
||||
|
||||
#[link(name = "env")]
|
||||
extern {
|
||||
fn storage_read(offset: u32, len: u32, dst: *mut u8) -> i32;
|
||||
fn storage_write(offset: u32, len: u32, src: *const u8) -> i32;
|
||||
fn storage_size() -> u32;
|
||||
}
|
||||
|
||||
pub fn read(offset: u32, dst: &mut [u8]) -> Result<u32, Error> {
|
||||
match unsafe {
|
||||
storage_read(offset, dst.len() as u32, dst.as_mut_ptr())
|
||||
} {
|
||||
x if x < 0 => Err(Error),
|
||||
x => Ok(x as u32),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(offset: u32, src: &[u8]) -> Result<u32, Error> {
|
||||
match unsafe {
|
||||
storage_write(offset, src.len() as u32, src.as_ptr())
|
||||
} {
|
||||
x if x < 0 => Err(Error),
|
||||
x => Ok(x as u32),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size() -> u32 {
|
||||
unsafe {
|
||||
storage_size()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(src: &[u8]) -> Result<u32, Error> {
|
||||
let sz = size();
|
||||
match write(sz, src) {
|
||||
Ok(_) => Ok(sz),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Safe (?) wrapper around call context
|
||||
struct CallArgs {
|
||||
context: Box<[u8]>,
|
||||
result: Vec<u8>,
|
||||
storage: Vec<u8>,
|
||||
}
|
||||
|
||||
unsafe fn read_ptr_mut(slc: &[u8]) -> *mut u8 {
|
||||
@ -40,23 +82,17 @@ fn write_ptr(dst: &mut [u8], ptr: *mut u8) {
|
||||
|
||||
impl CallArgs {
|
||||
pub fn from_raw(ptr: *mut u8) -> CallArgs {
|
||||
let desc_slice = unsafe { slice::from_raw_parts(ptr, 6 * 4) };
|
||||
let desc_slice = unsafe { slice::from_raw_parts(ptr, 4 * 4) };
|
||||
|
||||
let context_ptr = unsafe { read_ptr_mut(&desc_slice[0..4]) };
|
||||
let context_len = read_u32(&desc_slice[4..8]) as usize;
|
||||
|
||||
let storage_ptr = unsafe { read_ptr_mut(&desc_slice[8..12]) };
|
||||
let storage_len = read_u32(&desc_slice[12..16]) as usize;
|
||||
|
||||
let result_ptr = unsafe { read_ptr_mut(&desc_slice[16..20]) };
|
||||
let result_len = read_u32(&desc_slice[20..24]) as usize;
|
||||
let result_ptr = unsafe { read_ptr_mut(&desc_slice[8..12]) };
|
||||
let result_len = read_u32(&desc_slice[12..16]) as usize;
|
||||
|
||||
CallArgs {
|
||||
context: unsafe { Box::<[u8]>::from_raw(slice::from_raw_parts_mut(context_ptr, context_len)) },
|
||||
result: unsafe { Vec::from_raw_parts(result_ptr, result_len, result_len) },
|
||||
// todo: consider: storage (and result?) might also have initial allocation size passed in
|
||||
// the descriptor along with length
|
||||
storage: unsafe { Vec::from_raw_parts(storage_ptr, storage_len, storage_len) },
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,36 +104,27 @@ impl CallArgs {
|
||||
&mut self.result
|
||||
}
|
||||
|
||||
pub fn storage(&self) -> &[u8] {
|
||||
&self.storage
|
||||
}
|
||||
|
||||
pub fn storage_mut(&mut self) -> &mut Vec<u8> {
|
||||
&mut self.storage
|
||||
}
|
||||
|
||||
pub fn save(self, ptr: *mut u8) {
|
||||
let dst = unsafe { slice::from_raw_parts_mut(ptr, 6 * 4) };
|
||||
let context = self.context;
|
||||
let mut result = self.result;
|
||||
let mut storage = self.storage;
|
||||
|
||||
// context unmodified and memory is managed in calling code
|
||||
std::mem::forget(context);
|
||||
|
||||
write_ptr(dst, storage.as_mut_ptr());
|
||||
write_u32(dst, storage.len() as u32);
|
||||
// managed in calling code
|
||||
std::mem::forget(storage);
|
||||
|
||||
write_ptr(dst, result.as_mut_ptr());
|
||||
write_u32(dst, result.len() as u32);
|
||||
// managed in calling code
|
||||
std::mem::forget(result);
|
||||
if result.len() > 0 {
|
||||
// result
|
||||
write_ptr(dst, result.as_mut_ptr());
|
||||
write_u32(dst, result.len() as u32);
|
||||
// managed in calling code
|
||||
std::mem::forget(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn call(descriptor: *mut u8) {
|
||||
let context = CallArgs::from_raw(descriptor);
|
||||
let mut ctx = CallArgs::from_raw(descriptor);
|
||||
let _ = storage::append(ctx.context());
|
||||
*ctx.result_mut() = ctx.context().to_vec();
|
||||
}
|
Reference in New Issue
Block a user