Kagome
Polkadot Runtime Engine in C++17
module_instance_impl.cpp
Go to the documentation of this file.
1 
6 // Enables binaryen debug mode: every executed WASM instruction is printed out
7 // using Indenter (defined below) It is a massive amount of information, so it
8 // should be turned on only for specific cases when we need to follow web
9 // assembly execution very precisely. #define WASM_INTERPRETER_DEBUG
10 
12 
15 
16 #include <binaryen/wasm-interpreter.h>
17 
18 #ifdef WASM_INTERPRETER_DEBUG
19 
20 namespace wasm {
21 
22  // Indenter is declared in binaryen headers, but the following members are
23  // defined in its source files and only if WASM_INTERPRETER_DEBUG macro
24  // definition is set, which it isn't at the time of binaryen compilation.
25  // Therefore, to be able to use binaryen's debug mode we have to define
26  // indenter implementation ourselves. This design is unclear to me, but it
27  // does work.
28  int Indenter::indentLevel = 0;
29 
30  std::vector<std::string> indents = []() {
31  std::vector<std::string> indents;
32  for (size_t i = 0; i < 512; i++) {
33  indents.push_back(std::string(i, '-'));
34  }
35  return indents;
36  }();
37 
38  Indenter::Indenter(const char *entry) : entryName(entry) {
39  ++indentLevel;
40  }
41 
42  Indenter::~Indenter() {
43  print();
44  std::cout << "exit " << entryName << '\n';
45  --indentLevel;
46  }
47 
48  void Indenter::print() {
49  std::cout << indentLevel << ':' << indents[indentLevel];
50  }
51 } // namespace wasm
52 #endif
53 
54 #include <binaryen/wasm.h>
55 
56 #include "host_api/host_api.hpp"
58 
61  e) {
63  switch (e) {
65  return "Execution was ended in external function";
67  return "An error occurred during an export call execution";
69  return "Failed to obtain a global value";
70  }
71  return "Unknown ModuleInstance error";
72 }
73 
74 namespace kagome::runtime::binaryen {
75 
78  std::shared_ptr<wasm::Module> parent,
79  std::shared_ptr<RuntimeExternalInterface> rei,
80  const common::Hash256 &code_hash)
81  : env_{std::move(env)},
82  rei_{std::move(rei)},
83  parent_{std::move(parent)},
84  code_hash_(code_hash),
86  std::make_unique<wasm::ModuleInstance>(*parent_, rei_.get())},
87  logger_{log::createLogger("ModuleInstance", "binaryen")} {
88  BOOST_ASSERT(parent_);
89  BOOST_ASSERT(module_instance_);
90  BOOST_ASSERT(rei_);
91  }
92 
93  outcome::result<PtrSize> ModuleInstanceImpl::callExportFunction(
94  std::string_view name, common::BufferView encoded_args) const {
95  auto memory = env_.memory_provider->getCurrentMemory().value();
96 
97  PtrSize args{memory.get().storeBuffer(encoded_args)};
98 
99  const auto args_list =
100  wasm::LiteralList{wasm::Literal{args.ptr}, wasm::Literal{args.size}};
101  try {
102  const auto res = static_cast<uint64_t>(
103  module_instance_->callExport(wasm::Name{name.data()}, args_list)
104  .geti64());
105 
106  return PtrSize{res};
107 
108  } catch (wasm::ExitException &e) {
109  return Error::UNEXPECTED_EXIT;
110  } catch (wasm::TrapException &e) {
111  return Error::EXECUTION_ERROR;
112  }
113  }
114 
115  outcome::result<std::optional<WasmValue>> ModuleInstanceImpl::getGlobal(
116  std::string_view name) const {
117  try {
118  auto val = module_instance_->getExport(name.data());
119  switch (val.type) {
120  case wasm::Type::i32:
121  return WasmValue{val.geti32()};
122  case wasm::Type::i64:
123  return WasmValue{val.geti64()};
124  case wasm::Type::f32:
125  return WasmValue{val.getf32()};
126  case wasm::Type::f64:
127  return WasmValue{val.geti64()};
128  default:
129  logger_->error(
130  "Runtime function returned result of unsupported type: {}",
131  wasm::printType(val.type));
132  return std::nullopt;
133  }
134  } catch (wasm::TrapException &e) {
136  }
137  }
138 
140  return env_;
141  }
142 
143  outcome::result<void> ModuleInstanceImpl::resetEnvironment() {
144  env_.host_api->reset();
145  return outcome::success();
146  }
147 
149  DataSegmentProcessor const &callback) const {
150  for (auto &segment : parent_->memory.segments) {
151  wasm::Address offset =
152  (uint32_t)wasm::ConstantExpressionRunner<wasm::TrivialGlobalManager>(
153  module_instance_->globals)
154  .visit(segment.offset)
155  .value.geti32();
156  if (offset + segment.data.size()
157  > parent_->memory.initial * wasm::Memory::kPageSize) {
158  throw std::runtime_error("invalid offset when initializing memory");
159  }
160  callback(offset,
161  gsl::span<const uint8_t>(
162  reinterpret_cast<const uint8_t *>(segment.data.data()),
163  segment.data.size()));
164  }
165  }
166 
167 } // namespace kagome::runtime::binaryen
std::function< void(SegmentOffset, SegmentData)> DataSegmentProcessor
void forDataSegment(DataSegmentProcessor const &callback) const override
WasmPointer ptr
address of buffer
Definition: ptr_size.hpp:40
OUTCOME_CPP_DEFINE_CATEGORY(kagome::runtime::binaryen, ModuleInstanceImpl::Error, e)
std::unique_ptr< wasm::ModuleInstance > module_instance_
outcome::result< PtrSize > callExportFunction(std::string_view name, common::BufferView args) const override
outcome::result< std::optional< WasmValue > > getGlobal(std::string_view name) const override
std::shared_ptr< MemoryProvider > memory_provider
std::shared_ptr< RuntimeExternalInterface > rei_
InstanceEnvironment const & getEnvironment() const override
boost::variant< int32_t, int64_t, float, double > WasmValue
std::shared_ptr< host_api::HostApi > host_api
Logger createLogger(const std::string &tag)
Definition: logger.cpp:112
ModuleInstanceImpl(InstanceEnvironment &&env, std::shared_ptr< wasm::Module > parent, std::shared_ptr< RuntimeExternalInterface > rei, const common::Hash256 &code_hash)
outcome::result< void > resetEnvironment() override