diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index b1a4a405cf1e6d3527641153344cc268c7b10efd..4aa003f7ab8afea737c342e033713e2fd631433f 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace eosio { namespace chain { void apply_context::exec() @@ -8,7 +9,16 @@ void apply_context::exec() if( native ) { (*native)(*this); } else { - wlog( "WASM handler not yet implemented ${receiver} ${scope}::${name}", ("receiver",receiver)("scope",act.scope)("name",act.name) ); + const auto& a = mutable_controller.get_database().get(receiver); + + if (a.code.size() > 0) { + // get code from cache + auto code = mutable_controller.get_wasm_cache().checkout_scoped(a.code_version, a.code.data(), a.code.size()); + + // get wasm_interface + auto &wasm = wasm_interface::get(); + wasm.apply(code, *this); + } } } /// exec() diff --git a/libraries/chain/contracts/eosio_contract.cpp b/libraries/chain/contracts/eosio_contract.cpp index a4aa98148bc64b1b6fe20cff56f32febf309c3f2..3808ab5cbb3f2a82bba538e245d9b4464b127781 100644 --- a/libraries/chain/contracts/eosio_contract.cpp +++ b/libraries/chain/contracts/eosio_contract.cpp @@ -191,19 +191,25 @@ void apply_eosio_setcode(apply_context& context) { FC_ASSERT( act.vmtype == 0 ); FC_ASSERT( act.vmversion == 0 ); + auto code_id = fc::sha256::hash( act.code.data(), act.code.size() ); + const auto& account = db.get(act.account); // wlog( "set code: ${size}", ("size",act.code.size())); db.modify( account, [&]( auto& a ) { /** TODO: consider whether a microsecond level local timestamp is sufficient to detect code version changes*/ #warning TODO: update setcode message to include the hash, then validate it in validate - a.code_version = fc::sha256::hash( act.code.data(), act.code.size() ); + a.code_version = code_id; a.code.resize( act.code.size() ); memcpy( a.code.data(), act.code.data(), act.code.size() ); }); + // create an apply context for initialization apply_context init_context( context.mutable_controller, context.mutable_db, context.trx, context.act, act.account ); - wasm_interface::get().init( init_context ); + + // get code from cache + auto code = context.mutable_controller.get_wasm_cache().checkout_scoped(code_id, act.code.data(), act.code.size()); + wasm_interface::get().init( code, init_context ); } void apply_eosio_setabi(apply_context& context) { diff --git a/libraries/chain/include/eosio/chain/chain_controller.hpp b/libraries/chain/include/eosio/chain/chain_controller.hpp index 7d6e5976e1c8d57b202a9654d2d97577f60e3b27..0b0d0ae790cb151ce9ff83b7c6e2f50ee5854eb7 100644 --- a/libraries/chain/include/eosio/chain/chain_controller.hpp +++ b/libraries/chain/include/eosio/chain/chain_controller.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -27,6 +28,7 @@ namespace eosio { namespace chain { using database = chainbase::database; using boost::signals2::signal; + namespace contracts{ class chain_initializer; } enum validation_steps @@ -269,8 +271,9 @@ namespace eosio { namespace chain { const deque& pending()const { return _pending_transactions; } - - + wasm_cache& get_wasm_cache() { + return _wasm_cache; + } private: const apply_handler* find_apply_handler( account_name contract, scope_name scope, action_name act )const; @@ -396,6 +399,8 @@ namespace eosio { namespace chain { typedef pair handler_key; map< account_name, map > _apply_handlers; + + wasm_cache _wasm_cache; }; } } diff --git a/libraries/chain/include/eosio/chain/exceptions.hpp b/libraries/chain/include/eosio/chain/exceptions.hpp index 0be61eb71899650942d4ec21b1048bcc9dab68cd..4a8a8cda56b34cdbf59672e2a21ee6d07255cdf1 100644 --- a/libraries/chain/include/eosio/chain/exceptions.hpp +++ b/libraries/chain/include/eosio/chain/exceptions.hpp @@ -43,6 +43,7 @@ namespace eosio { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( tx_msgs_code_exceeded, eosio::chain::transaction_exception, 3030019, "Number of transaction messages per code account has been exceeded" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_read_scope, eosio::chain::transaction_exception, 3030020, "missing required read scope" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_write_scope, eosio::chain::transaction_exception, 3030021, "missing required write scope" ) + FC_DECLARE_DERIVED_EXCEPTION( wasm_execution_error, eosio::chain::transaction_exception, 3030022, "Runtime Error Processing WASM" ) FC_DECLARE_DERIVED_EXCEPTION( invalid_pts_address, eosio::chain::utility_exception, 3060001, "invalid pts address" ) FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, eosio::chain::chain_exception, 37006, "insufficient feeds" ) diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index 23a53ed3d5c96e241258745062361b38f7686a3f..66794b6ab39d5aa4ecebe92c29405ff83b4c5e81 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -12,41 +12,101 @@ namespace eosio { namespace chain { class intrinsics_accessor; /** - * @class wasm_interface - * - * EOS.IO uses the wasm-jit library to evaluate web assembly code. This library relies - * upon a singlton thread-local interface which means there can be only one instance - * per thread. + * @class wasm_cache * + * This class manages compilation, re-use and memory sanitation for WASM contracts + * As the same code can be running on many threads in parallel, some contracts will have multiple + * copies. */ - class wasm_interface { + class wasm_cache { public: - static wasm_interface& get(); + wasm_cache(); + ~wasm_cache(); + + /** + * an opaque code entry used in the wasm_interface class + */ + struct entry; - Runtime::MemoryInstance* memory()const; - uint32_t memory_size()const; + /** + * Checkout the desired code from the cache. Code is idenfied via a digest of the wasm binary + * + * @param code_id - a digest of the wasm_binary bytes + * @param wasm_binary - a pointer to the wasm_binary bytes + * @param wasm_binary_size - the size of the wasm_binary bytes array + * @return an entry which can be immediately used by the wasm_interface to execute contract code + */ + entry &checkout( const digest_type& code_id, const char* wasm_binary, size_t wasm_binary_size ); /** - * Will initalize the given code or used a cached version if one exists for - * the given codeid. + * Return an entry to the cache so that future checkouts may retrieve it * - * @param wasmcode - code in wasm format + * @param code_id - a digest of the wasm_binary bytes + * @param code - the entry which should be considered invalid post-call */ - void load( digest_type codeid, const char* wasmcode, size_t codesize ); + void checkin( const digest_type& code_id, entry& code ); /** - * If there exists a cached version of the code for codeid then it is released + * RAII wrapper to make sure that the cache entries are returned regardless of exceptions etc */ - void unload( digest_type codeid ); + struct scoped_entry { + explicit scoped_entry(const digest_type& code_id, entry &code, wasm_cache &cache) + :code_id(code_id) + ,code(code) + ,cache(cache) + {} + + ~scoped_entry() { + cache.checkin(code_id, code); + } + operator entry&() { + return code; + } + + digest_type code_id; + entry& code; + wasm_cache& cache; + }; /** + * Checkout the desired code from the cache. Code is idenfied via a digest of the wasm binary + * this method will wrap the code in an RAII construct so that it will automatically + * return to the cache when it falls out of scope + * + * @param code_id - a digest of the wasm_binary bytes + * @param wasm_binary - a pointer to the wasm_binary bytes + * @param wasm_binary_size - the size of the wasm_binary bytes array + * @return an entry which can be immediately used by the wasm_interface to execute contract code + */ + scoped_entry checkout_scoped(const digest_type& code_id, const char* wasm_binary, size_t wasm_binary_size) { + return scoped_entry(code_id, checkout(code_id, wasm_binary, wasm_binary_size), *this); + } + + + private: + unique_ptr _my; + }; + + /** + * @class wasm_interface + * + * EOS.IO uses the wasm-jit library to evaluate web assembly code. This library relies + * upon a singlton thread-local interface which means there can be only one instance + * per thread. + * + */ + class wasm_interface { + public: + static wasm_interface& get(); + + /** * Calls the init() method on the currently loaded code * * @param context - the interface by which the contract can interact * with blockchain state. */ - void init( apply_context& context ); + void init( wasm_cache::entry& code, apply_context& context ); /** * Calls the apply() method on the currently loaded code @@ -54,11 +114,11 @@ namespace eosio { namespace chain { * @param context - the interface by which the contract can interact * with blockchain state. */ - void apply( apply_context& context ); + void apply( wasm_cache::entry& code, apply_context& context ); /** */ - void error( apply_context& context ); + void error( wasm_cache::entry& code, apply_context& context ); private: wasm_interface(); @@ -66,5 +126,4 @@ namespace eosio { namespace chain { friend class eosio::chain::intrinsics_accessor; }; - } } // eosio::chain diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index 10524db5b64fb6cc71124e51ab7de6ebdd965e06..b60f9dbbe813d2be3b2c5ab5a99aed4e8446dc28 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include "Runtime/Runtime.h" #include "IR/Types.h" @@ -9,31 +10,33 @@ namespace eosio { namespace chain { using namespace IR; using namespace Runtime; +using namespace fc; -struct module_state { - ModuleInstance* instance = nullptr; - Module* module = nullptr; - apply_context* context = nullptr; - int mem_start = 0; - int mem_end = 1<<16; - vector init_memory; - fc::sha256 code_version; +struct wasm_cache::entry { + entry(ModuleInstance* instance, Module* module) + :instance(instance) + ,module(module) + {} + + ModuleInstance* instance; + Module* module; }; -struct wasm_interface_impl { - map code_cache; - module_state* current_state = nullptr; +struct wasm_context { + wasm_cache::entry& code; + apply_context& context; }; -wasm_interface::wasm_interface() - :my( new wasm_interface_impl() ) { -} +struct wasm_interface_impl { + optional current_context; + void call(const string& entry_point, const vector& args, wasm_cache::entry& code, apply_context &context); +}; class intrinsics_accessor { public: - static module_state& get_module_state(wasm_interface& wasm) { - FC_ASSERT(wasm.my->current_state != nullptr); - return *wasm.my->current_state; + static wasm_context& get_context(wasm_interface& wasm) { + FC_ASSERT(wasm.my->current_context.valid()); + return *wasm.my->current_context; } }; @@ -213,7 +216,7 @@ struct intrinsic_invoker_impl, size_t, Inputs...>, template static Ret translate_one(wasm_interface& wasm, Inputs... rest, Translated... translated, I32 ptr, I32 size) { - auto mem = getDefaultMemory(intrinsics_accessor::get_module_state(wasm).instance); + auto mem = getDefaultMemory(intrinsics_accessor::get_context(wasm).code.instance); size_t length = size_t(size); T* base = memoryArrayPtr( mem, ptr, length ); return Then(wasm, array_ptr{base}, length, rest..., translated...); @@ -242,7 +245,7 @@ struct intrinsic_invoker_impl, std::tuple static Ret translate_one(wasm_interface& wasm, Inputs... rest, Translated... translated, I32 ptr) { - auto mem = getDefaultMemory(intrinsics_accessor::get_module_state(wasm).instance); + auto mem = getDefaultMemory(intrinsics_accessor::get_context(wasm).code.instance); T* base = memoryArrayPtr( mem, ptr, 1 ); return Then(wasm, base, rest..., translated...); }; @@ -272,7 +275,7 @@ struct intrinsic_invoker_impl, std::tuple( mem, ptr ); return Then(wasm, base, rest..., translated...); } diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 6a1326a5f17aa0cc821a70d1c88f33d6f6f56b3b..c515671d17240978419ae81727b578518438036b 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -16,11 +17,17 @@ #include "IR/Validate.h" #include "IR/Types.h" +#include +#include + #include +#include +#include + using namespace IR; using namespace Runtime; - +using boost::asio::io_service; namespace eosio { namespace chain { @@ -46,137 +53,338 @@ namespace eosio { namespace chain { } }; + /** + * Implementation class for the wasm cache + * it is responsible for compiling and storing instances of wasm code for use + * + */ + struct wasm_cache_impl { + wasm_cache_impl() + :_ios() + ,_work(_ios) + { + _utility_thread = std::thread([](io_service* ios){ + ios->run(); + }, &_ios); + } + /** + * this must wait for all work to be done otherwise it may destroy memory + * referenced by other threads + * + * Expectations on wasm_cache dictate that all available code has been + * returned before this can be destroyed + */ + ~wasm_cache_impl() { + _work.reset(); + _ios.stop(); + _utility_thread.join(); + freeUnreferencedObjects({}); + } - std::mutex global_load_mutex; - wasm_interface& wasm_interface::get() { - static bool init_once = [](){ Runtime::init(); return true; }(); - boost::ignore_unused(init_once); + /** + * internal tracking structure which deduplicates memory images + * and tracks available vs in-use entries. + * + * The instances array has two sections, "available" instances + * are in the front of the vector and anything at an index of + * available_instances or greater is considered "in use" + * + * instances are stored as pointers so that their positions + * in the array can be moved without invaliding references to + * the instance handed out to other threads + */ + struct code_info { + // a clean image of the memory used to sanitize things on checkin + size_t mem_start = 0; + size_t mem_end = 1<<16; + vector mem_image; + + // all existing instances of this code + vector> instances; + size_t available_instances = 0; + }; + + using optional_info_ref = optional>; + using optional_entry_ref = optional>; + + /** + * Convenience method for running code with the _cache_lock and releaseint that lock + * when the code completes + * + * @param f - lambda to execute + * @return - varies depending on the signature of the lambda + */ + template + auto with_lock(F f) { + std::lock_guard lock(_cache_lock); + return f(); + }; + + /** + * Fetch the tracking struct given a code_id if it exists + * + * @param code_id + * @return + */ + optional_info_ref fetch_info(const digest_type& code_id) { + return with_lock([&,this](){ + auto iter = _cache.find(code_id); + if (iter != _cache.end()) { + return optional_info_ref(iter->second); + } + + return optional_info_ref(); + }); + } - thread_local wasm_interface* single = nullptr; - if( !single ) { - single = new wasm_interface(); + /** + * Opportunistically fetch an available instance of the code; + * @param code_id - the id of the code to fetch + * @return - reference to the entry when one is available + */ + optional_entry_ref try_fetch_entry(const digest_type& code_id) { + return with_lock([&,this](){ + auto iter = _cache.find(code_id); + if (iter != _cache.end() && iter->second.available_instances > 0) { + auto &ptr = iter->second.instances.at(iter->second.available_instances--); + return optional_entry_ref(*ptr); + } + + return optional_entry_ref(); + }); } - return *single; + + /** + * Fetch a copy of the code, this is guaranteed to return an entry IF the code is compilable. + * In order to do that in safe way this code may cause the calling thread to sleep while a new + * version of the code is compiled and inserted into the cache + * + * @param code_id - the id of the code to fetch + * @param wasm_binary - the binary for the wasm + * @param wasm_binary_size - the size of the binary + * @return reference to a usable cache entry + */ + wasm_cache::entry& fetch_entry(const digest_type& code_id, const char* wasm_binary, size_t wasm_binary_size) { + std::condition_variable condition; + optional_entry_ref result; + std::exception_ptr error; + + // compilation is not thread safe, so we dispatch it to a io_service running on a single thread to + // queue up and synchronize compilations + _ios.post([&,this](){ + // check to see if someone returned what we need before making a new one + auto pending_result = try_fetch_entry(code_id); + std::exception_ptr pending_error; + + if (!pending_result) { + // time to compile a brand new (maybe first) copy of this code + Module* module = new Module(); + ModuleInstance* instance = nullptr; + size_t mem_end; + vector mem_image; + + try { + Serialization::MemoryInputStream stream((const U8 *) wasm_binary, wasm_binary_size); + WASM::serializeWithInjection(stream, *module); + + root_resolver resolver; + LinkResult link_result = linkModule(*module, resolver); + instance = instantiateModule(*module, std::move(link_result.resolvedImports)); + FC_ASSERT(instance != nullptr); + + auto current_memory = Runtime::getDefaultMemory(instance); + + char *mem_ptr = &memoryRef(current_memory, 0); + const auto allocated_memory = Runtime::getDefaultMemorySize(instance); + for (uint64_t i = 0; i < allocated_memory; ++i) { + if (mem_ptr[i]) { + mem_end = i + 1; + } + } + + mem_image.resize(mem_end); + memcpy(mem_image.data(), mem_ptr, mem_end); + } catch (...) { + pending_error = std::current_exception(); + } + + if (pending_error == nullptr) { + // grab the lock and put this in the cache as unavailble + with_lock([&,this]() { + // find or create a new entry + auto iter = _cache.emplace(code_id, code_info { + .mem_end = mem_end, + .mem_image = std::move(mem_image) + }).first; + + iter->second.instances.emplace_back(std::make_unique(instance, module)); + pending_result = optional_entry_ref(*iter->second.instances.back().get()); + }); + } + } + + // publish result under lock + with_lock([&](){ + if (pending_error != nullptr) { + error = pending_error; + } else { + result = pending_result; + } + }); + + condition.notify_all(); + }); + + // wait for the other thread to compile a copy for us + { + std::unique_lock lock(_cache_lock); + condition.wait(lock, [&]{ + return error != nullptr || result.valid(); + }); + } + + try { + if (error != nullptr) { + std::rethrow_exception(error); + } else { + return (*result).get(); + } + } FC_RETHROW_EXCEPTIONS(error, "error compiling WASM for code with hash: ${code_id}", ("code_id", code_id)); + } + + /** + * return an entry to the cache. The entry is presumed to come back in a "dirty" state and must be + * sanitized before returning to the "available" state. This sanitization is done asynchronously so + * as not to delay the current executing thread. + * + * @param code_id - the code Id associated with the instance + * @param entry - the entry to return + */ + void return_entry(const digest_type& code_id, wasm_cache::entry& entry) { + _ios.post([&,this](){ + // sanitize by reseting the memory that may now be dirty + auto& info = (*fetch_info(code_id)).get(); + char* memstart = &memoryRef( getDefaultMemory(entry.instance), 0 ); + memset( memstart + info.mem_end, 0, ((1<<16) - info.mem_end) ); + memcpy( memstart, info.mem_image.data(), info.mem_end); + + // under a lock, put this entry back in the available instances side of the instances vector + with_lock([&,this](){ + // walk the vector and find this entry + auto iter = info.instances.begin(); + while (iter->get() != &entry) { + ++iter; + } + + FC_ASSERT(iter != info.instances.end(), "Checking in a WASM enty that was not created properly!"); + + auto first_unavailable = (info.instances.begin() + info.available_instances); + if (iter != first_unavailable) { + std::swap(iter, first_unavailable); + } + info.available_instances++; + }); + }); + } + + // mapping of digest to an entry for the code + map _cache; + std::mutex _cache_lock; + + // compilation and cleanup thread + std::thread _utility_thread; + io_service _ios; + optional _work; + }; + + wasm_cache::wasm_cache() + :_my( new wasm_cache_impl() ) { } + wasm_cache::~wasm_cache() = default; - void wasm_interface::load( digest_type codeid, const char* code, size_t codesize ) - { - std::unique_lock lock(global_load_mutex); + wasm_cache::entry &wasm_cache::checkout( const digest_type& code_id, const char* wasm_binary, size_t wasm_binary_size ) { + // see if there is an avaialble entry in the cache + auto result = _my->try_fetch_entry(code_id); - FC_ASSERT( codeid != digest_type() ); - auto& state = my->code_cache[codeid]; - if( state.code_version == codeid ) { - my->current_state = &state; - return; /// already cached + if (result) { + return (*result).get(); } - state.module = new IR::Module(); - - try { - Serialization::MemoryInputStream stream((const U8*)code,codesize); - WASM::serializeWithInjection(stream,*state.module); - - root_resolver resolver; - LinkResult link_result = linkModule( *state.module, resolver ); - state.instance = instantiateModule( *state.module, move(link_result.resolvedImports) ); - FC_ASSERT( state.instance ); - - auto current_memory = Runtime::getDefaultMemory(state.instance); - - char* memstart = &memoryRef( current_memory, 0 ); - const auto allocated_memory = Runtime::getDefaultMemorySize(state.instance); - for( uint64_t i = 0; i < allocated_memory; ++i ) - { - if( memstart[i] ) { - state.mem_end = i+1; - } - } - - state.init_memory.resize(state.mem_end); - memcpy( state.init_memory.data(), memstart, state.mem_end ); - state.code_version = codeid; - } - catch(Serialization::FatalSerializationException exception) - { - std::cerr << "Error deserializing WebAssembly binary file:" << std::endl; - std::cerr << exception.message << std::endl; - throw; - } - catch(IR::ValidationException exception) - { - std::cerr << "Error validating WebAssembly binary file:" << std::endl; - std::cerr << exception.message << std::endl; - throw; - } - catch(std::bad_alloc) - { - std::cerr << "Memory allocation failed: input is likely malformed" << std::endl; - throw; - } - my->current_state = &state; - } /// wasm_interface::load - - void wasm_interface::init( apply_context& context ) - { try { - try { - FunctionInstance* init = asFunctionNullable(getInstanceExport(my->current_state->instance,"init")); - if( !init ) return; /// if not found then it is a no-op - - //checktimeStart = fc::time_point::now(); - - const FunctionType* functype = getFunctionType(init); - FC_ASSERT( functype->parameters.size() == 0 ); - - std::vector args(0); - - Runtime::invokeFunction(init,args); - - } catch( const Runtime::Exception& e ) { - edump((string(describeExceptionCause(e.cause)))); - edump((e.callStack)); - throw; + return _my->fetch_entry(code_id, wasm_binary, wasm_binary_size); + } + + + void wasm_cache::checkin(const digest_type& code_id, entry& code ) { + _my->return_entry(code_id, code); + } + + /** + * RAII wrapper to make sure that the context is cleaned up on exception + */ + struct scoped_context { + template + scoped_context(optional &context, Args&... args) + :context(context) + { + context = wasm_context{ args... }; } - } FC_CAPTURE_AND_RETHROW() } /// wasm_interface::init - - void wasm_interface::apply( apply_context& context ) - { try { - try { - FunctionInstance* call = asFunctionNullable(getInstanceExport(my->current_state->instance,"apply") ); - if( !call ) { - return; - } - //FC_ASSERT( apply, "no entry point found for ${call}", ("call", std::string(name)) ); - FC_ASSERT( getFunctionType(call)->parameters.size() == 2 ); + ~scoped_context() { + context.reset(); + } + + optional& context; + }; + + void wasm_interface_impl::call(const string& entry_point, const vector& args, wasm_cache::entry& code, apply_context &context) + try { + FunctionInstance* call = asFunctionNullable(getInstanceExport(code.instance,entry_point) ); + if( !call ) { + return; + } + + FC_ASSERT( getFunctionType(call)->parameters.size() == 2 ); - // idump((current_validate_context->msg.code)(current_validate_context->msg.type)(current_validate_context->code)); - vector args = { Value(uint64_t(context.act.scope)), - Value(uint64_t(context.act.name)) }; + auto context_guard = scoped_context(current_context, code, context); + Runtime::invokeFunction(call,args); + } catch( const Runtime::Exception& e ) { + FC_THROW_EXCEPTION(wasm_execution_error, + "cause: ${cause}\n${callstack}", + ("cause", string(describeExceptionCause(e.cause))) + ("callstack", e.callStack)); + } FC_CAPTURE_AND_RETHROW() - auto& state = *my->current_state; - char* memstart = &memoryRef( getDefaultMemory(my->current_state->instance), 0 ); - memset( memstart + state.mem_end, 0, ((1<<16) - state.mem_end) ); - memcpy( memstart, state.init_memory.data(), state.mem_end); + wasm_interface::wasm_interface() + :my( new wasm_interface_impl() ) { + } - //checktimeStart = fc::time_point::now(); - my->current_state->context = &context; - Runtime::invokeFunction(call,args); - my->current_state->context = nullptr; + wasm_interface& wasm_interface::get() { + static bool init_once = [](){ Runtime::init(); return true; }(); + boost::ignore_unused(init_once); - } catch( const Runtime::Exception& e ) { - edump((std::string(describeExceptionCause(e.cause)))); - edump((e.callStack)); - throw; + thread_local wasm_interface* single = nullptr; + if( !single ) { + single = new wasm_interface(); } - } FC_CAPTURE_AND_RETHROW() } /// wasm_interface::apply + return *single; + } + - Runtime::MemoryInstance* wasm_interface::memory()const { - return Runtime::getDefaultMemory( my->current_state->instance ); + void wasm_interface::init( wasm_cache::entry& code, apply_context& context ) { + my->call("init", {}, code, context); } - uint32_t wasm_interface::memory_size()const { - return Runtime::getDefaultMemorySize( my->current_state->instance ); + + void wasm_interface::apply( wasm_cache::entry& code, apply_context& context ) { + vector args = { Value(uint64_t(context.act.scope)), + Value(uint64_t(context.act.name)) }; + my->call("apply", args, code, context); + } + + void wasm_interface::error( wasm_cache::entry& code, apply_context& context ) { + vector args = { /* */ }; + my->call("error", args, code, context); } #if 0 @@ -327,7 +535,7 @@ class intrinsics { public: intrinsics(wasm_interface &wasm) :wasm(wasm) - ,context(*intrinsics_accessor::get_module_state(wasm).context) + ,context(intrinsics_accessor::get_context(wasm).context) {} int read_action(array_ptr memory, size_t size) {