From 8ca9fbe612b68a1595f439adc3f974cb90b849a2 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 24 Apr 2017 21:43:53 +0300 Subject: [PATCH] partly working --- runner/index.html | 206 +++++++++++++++++++++++++++++++++---------- samples/contract3.rs | 85 ++++++++++++------ 2 files changed, 217 insertions(+), 74 deletions(-) diff --git a/runner/index.html b/runner/index.html index 39f15d1..0988de9 100644 --- a/runner/index.html +++ b/runner/index.html @@ -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); } ); diff --git a/samples/contract3.rs b/samples/contract3.rs index 40a772f..c409e0d 100644 --- a/samples/contract3.rs +++ b/samples/contract3.rs @@ -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 { + 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 { + 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 { + 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, - storage: Vec, } 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 { - &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(); } \ No newline at end of file