提交 cf96345f 编写于 作者: B Bart Wyatt

Merge branch 'feature/wasm-cache' into eos-noon

#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/chain_controller.hpp>
#include <eosio/chain/wasm_interface.hpp>
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<account_object,by_name>(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()
......
......@@ -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<account_object,by_name>(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) {
......
......@@ -18,6 +18,7 @@
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/chain/contracts/genesis_state.hpp>
#include <eosio/chain/wasm_interface.hpp>
#include <fc/log/logger.hpp>
......@@ -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<signed_transaction>& 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<scope_name,action_name> handler_key;
map< account_name, map<handler_key, apply_handler> > _apply_handlers;
wasm_cache _wasm_cache;
};
} }
......@@ -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" )
......
......@@ -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<struct wasm_cache_impl> _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
#pragma once
#include <eosio/chain/wasm_interface.hpp>
#include <fc/optional.hpp>
#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<char> 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<digest_type, module_state> 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<wasm_context> current_context;
void call(const string& entry_point, const vector<Value>& 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<Ret, std::tuple<array_ptr<T>, size_t, Inputs...>,
template<then_type Then>
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<T>( mem, ptr, length );
return Then(wasm, array_ptr<T>{base}, length, rest..., translated...);
......@@ -242,7 +245,7 @@ struct intrinsic_invoker_impl<Ret, std::tuple<T*, Inputs...>, std::tuple<Transla
template<then_type Then>
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<T>( mem, ptr, 1 );
return Then(wasm, base, rest..., translated...);
};
......@@ -272,7 +275,7 @@ struct intrinsic_invoker_impl<Ret, std::tuple<T&, Inputs...>, std::tuple<Transla
static Ret translate_one(wasm_interface& wasm, Inputs... rest, Translated... translated, I32 ptr) {
// references cannot be created for null pointers
FC_ASSERT(ptr != 0);
auto mem = getDefaultMemory(intrinsics_accessor::get_module_state(wasm).instance);
auto mem = getDefaultMemory(intrinsics_accessor::get_context(wasm).code.instance);
T& base = memoryRef<T>( mem, ptr );
return Then(wasm, base, rest..., translated...);
}
......
#include <eosio/chain/wasm_interface.hpp>
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/exceptions.hpp>
#include <boost/core/ignore_unused.hpp>
#include <eosio/chain/wasm_interface_private.hpp>
#include <fc/exception/exception.hpp>
......@@ -16,11 +17,17 @@
#include "IR/Validate.h"
#include "IR/Types.h"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <mutex>
#include <thread>
#include <condition_variable>
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<char> mem_image;
// all existing instances of this code
vector<unique_ptr<wasm_cache::entry>> instances;
size_t available_instances = 0;
};
using optional_info_ref = optional<std::reference_wrapper<code_info>>;
using optional_entry_ref = optional<std::reference_wrapper<wasm_cache::entry>>;
/**
* 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<typename F>
auto with_lock(F f) {
std::lock_guard<std::mutex> 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<char> 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<char>(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<wasm_cache::entry>(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<std::mutex> 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<char>( 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<digest_type, code_info> _cache;
std::mutex _cache_lock;
// compilation and cleanup thread
std::thread _utility_thread;
io_service _ios;
optional<io_service::work> _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<std::mutex> 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<char>( 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<Value> 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<typename ...Args>
scoped_context(optional<wasm_context> &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<wasm_context>& context;
};
void wasm_interface_impl::call(const string& entry_point, const vector<Value>& 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<Value> 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<char>( 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<Value> 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<Value> 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<char> memory, size_t size) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册