Coverage Report

Created: 2026-09-28 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/executor/executor.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "executor/executor.h"
5
6
#include "common/errinfo.h"
7
#include "common/spdlog.h"
8
#include "runtime/instance/module.h"
9
#include "system/stacktrace.h"
10
11
#include <algorithm>
12
#include <string>
13
#include <string_view>
14
#include <vector>
15
16
using namespace std::literals;
17
18
namespace WasmEdge {
19
namespace Executor {
20
21
namespace {
22
23
/// Render a recorded stack trace as "module:index" frames. Modules without a
24
/// registered name are numbered by their order of appearance in the trace.
25
0
void dumpStackTrace(Span<const StackTraceEntry> Stack) noexcept {
26
0
  std::vector<const Runtime::Instance::ModuleInstance *> Anonymous;
27
0
  std::string Out;
28
0
  for (size_t I = 0; I < Stack.size(); ++I) {
29
0
    if (I != 0) {
30
0
      Out += ", "sv;
31
0
    }
32
0
    const auto *Module = Stack[I].Module;
33
0
    std::string_view Name =
34
0
        Module ? Module->getModuleName() : std::string_view{};
35
0
    if (!Name.empty()) {
36
0
      Out += fmt::format("{}:{}"sv, Name, Stack[I].FuncIndex);
37
0
    } else {
38
0
      auto Iter = std::find(Anonymous.begin(), Anonymous.end(), Module);
39
0
      size_t Ordinal;
40
0
      if (Iter == Anonymous.end()) {
41
0
        Ordinal = Anonymous.size();
42
0
        Anonymous.push_back(Module);
43
0
      } else {
44
0
        Ordinal = static_cast<size_t>(Iter - Anonymous.begin());
45
0
      }
46
0
      Out += fmt::format("module[{}]:{}"sv, Ordinal, Stack[I].FuncIndex);
47
0
    }
48
0
  }
49
0
  spdlog::error("calling stack:{}"sv, Out);
50
0
}
51
52
} // namespace
53
54
/// Instantiate a WASM Module. See "include/executor/executor.h".
55
Expect<std::unique_ptr<Runtime::Instance::ModuleInstance>>
56
Executor::instantiateModule(Runtime::StoreManager &StoreMgr,
57
0
                            const AST::Module &Mod) {
58
0
  return instantiate(StoreMgr, Mod).map_error([this](auto E) {
59
    // If statistics are enabled, dump them here.
60
    // When an error occurs, subsequent execution will not run.
61
0
    if (Stat) {
62
0
      Stat->dumpToLog(Conf);
63
0
    }
64
0
    return E;
65
0
  });
66
0
}
67
68
/// Register a named WASM module. See "include/executor/executor.h".
69
Expect<std::unique_ptr<Runtime::Instance::ModuleInstance>>
70
Executor::registerModule(Runtime::StoreManager &StoreMgr,
71
0
                         const AST::Module &Mod, std::string_view Name) {
72
0
  return instantiate(StoreMgr, Mod, Name).map_error([this](auto E) {
73
    // If statistics are enabled, dump them here.
74
    // When an error occurs, subsequent execution will not run.
75
0
    if (Stat) {
76
0
      Stat->dumpToLog(Conf);
77
0
    }
78
0
    return E;
79
0
  });
80
0
}
81
82
/// Register an instantiated module. See "include/executor/executor.h".
83
Expect<void>
84
Executor::registerModule(Runtime::StoreManager &StoreMgr,
85
0
                         const Runtime::Instance::ModuleInstance &ModInst) {
86
0
  return StoreMgr.registerModule(&ModInst).map_error([](auto E) {
87
0
    spdlog::error(E);
88
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module));
89
0
    return E;
90
0
  });
91
0
}
92
93
/// Register an instantiated module under an alias name.
94
Expect<void>
95
Executor::registerModule(Runtime::StoreManager &StoreMgr,
96
                         const Runtime::Instance::ModuleInstance &ModInst,
97
0
                         std::string_view Name) {
98
0
  return StoreMgr.registerModule(&ModInst, Name).map_error([](auto E) {
99
0
    spdlog::error(E);
100
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module));
101
0
    return E;
102
0
  });
103
0
}
104
105
/// Instantiate a Component. See "include/executor/executor.h".
106
Expect<std::unique_ptr<Runtime::Instance::ComponentInstance>>
107
Executor::instantiateComponent(Runtime::StoreManager &StoreMgr,
108
0
                               const AST::Component::Component &Comp) {
109
0
  return instantiate(StoreMgr, Comp);
110
0
}
111
112
/// Register a named Component. See "include/executor/executor.h".
113
Expect<std::unique_ptr<Runtime::Instance::ComponentInstance>>
114
Executor::registerComponent(Runtime::StoreManager &StoreMgr,
115
                            const AST::Component::Component &Comp,
116
0
                            std::string_view Name) {
117
0
  return instantiate(StoreMgr, Comp, Name);
118
0
}
119
120
/// Register an instantiated Component. See "include/executor/executor.h".
121
Expect<void> Executor::registerComponent(
122
    Runtime::StoreManager &StoreMgr,
123
0
    const Runtime::Instance::ComponentInstance &CompInst) {
124
0
  return StoreMgr.registerComponent(&CompInst).map_error([](auto E) {
125
0
    spdlog::error(E);
126
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Component));
127
0
    return E;
128
0
  });
129
0
}
130
131
/// Register a host function which will be invoked before calling a
132
/// host function.
133
Expect<void> Executor::registerPreHostFunction(
134
0
    void *HostData = nullptr, std::function<void(void *)> HostFunc = nullptr) {
135
0
  HostFuncHelper.setPreHost(HostData, HostFunc);
136
0
  return {};
137
0
}
138
139
/// Register a host function which will be invoked after calling a
140
/// host function.
141
Expect<void> Executor::registerPostHostFunction(
142
0
    void *HostData = nullptr, std::function<void(void *)> HostFunc = nullptr) {
143
0
  HostFuncHelper.setPostHost(HostData, HostFunc);
144
0
  return {};
145
0
}
146
147
/// Invoke function. See "include/executor/executor.h".
148
Expect<std::vector<std::pair<ValVariant, ValType>>>
149
Executor::invoke(const Runtime::Instance::FunctionInstance *FuncInst,
150
                 Span<const ValVariant> Params,
151
0
                 Span<const ValType> ParamTypes) {
152
0
  if (unlikely(FuncInst == nullptr)) {
153
0
    spdlog::error(ErrCode::Value::FuncNotFound);
154
0
    return Unexpect(ErrCode::Value::FuncNotFound);
155
0
  }
156
157
  // Matching arguments and function type.
158
0
  const auto &FuncType = FuncInst->getFuncType();
159
0
  const auto &PTypes = FuncType.getParamTypes();
160
0
  const auto &RTypes = FuncType.getReturnTypes();
161
  // The defined type list may be empty if the function is an independent
162
  // function instance, that is, the module instance will be nullptr. In this
163
  // case, all value types are number types or abstract heap types.
164
  //
165
  // If a function belongs to a component instance, its type should already be
166
  // converted, so the type list is not needed.
167
0
  WasmEdge::Span<const WasmEdge::AST::SubType *const> TypeList = {};
168
0
  if (FuncInst->getModule()) {
169
0
    TypeList = FuncInst->getModule()->getTypeList();
170
0
  }
171
0
  if (!AST::TypeMatcher::matchTypes(TypeList, ParamTypes, PTypes)) {
172
0
    spdlog::error(ErrCode::Value::FuncSigMismatch);
173
0
    spdlog::error(ErrInfo::InfoMismatch(
174
0
        PTypes, RTypes, std::vector(ParamTypes.begin(), ParamTypes.end()),
175
0
        RTypes));
176
0
    return Unexpect(ErrCode::Value::FuncSigMismatch);
177
0
  }
178
179
  // Check the reference value validation.
180
0
  for (uint32_t I = 0; I < ParamTypes.size(); ++I) {
181
0
    if (ParamTypes[I].isRefType() && (!ParamTypes[I].isNullableRefType() &&
182
0
                                      Params[I].get<RefVariant>().isNull())) {
183
0
      spdlog::error(ErrCode::Value::NonNullRequired);
184
0
      spdlog::error("    Cannot pass a null reference as argument of {}."sv,
185
0
                    ParamTypes[I]);
186
0
      return Unexpect(ErrCode::Value::NonNullRequired);
187
0
    }
188
0
  }
189
190
0
  Runtime::StackManager StackMgr;
191
192
  // Call runFunction.
193
0
  EXPECTED_TRY(runFunction(StackMgr, *FuncInst, Params).map_error([](auto E) {
194
0
    if (E != ErrCode::Value::Terminated) {
195
0
      dumpStackTrace(
196
0
          Span<const StackTraceEntry>{StackTrace}.first(StackTraceSize));
197
0
    }
198
0
    return E;
199
0
  }));
200
201
  // Get return values.
202
0
  std::vector<std::pair<ValVariant, ValType>> Returns(RTypes.size());
203
0
  for (uint32_t I = 0; I < RTypes.size(); ++I) {
204
0
    auto Val = StackMgr.pop();
205
0
    const auto &RType = RTypes[RTypes.size() - I - 1];
206
0
    if (RType.isRefType()) {
207
      // For the reference type cases of the return values, they should be
208
      // transformed into abstract heap types due to the opaque of type indices.
209
0
      auto &RefType = Val.get<RefVariant>().getType();
210
0
      if (RefType.isExternalized()) {
211
        // First handle the forced externalized value type case.
212
0
        RefType = ValType(TypeCode::Ref, TypeCode::ExternRef);
213
0
      }
214
0
      if (!RefType.isAbsHeapType()) {
215
        // The instance must not be nullptr because the null references are
216
        // already dynamic typed into the top abstract heap type.
217
0
        auto *Inst =
218
0
            Val.get<RefVariant>().getPtr<Runtime::Instance::CompositeBase>();
219
0
        assuming(Inst);
220
        // The ModInst may be nullptr only in the independent host function
221
        // instance. Therefore the module instance here must not be nullptr
222
        // because the independent host function instance cannot be imported and
223
        // be referred by instructions.
224
0
        const auto *ModInst = Inst->getModule();
225
0
        const auto *DefType = *ModInst->getType(RefType.getTypeIndex());
226
0
        RefType =
227
0
            ValType(RefType.getCode(), DefType->getCompositeType().expand());
228
0
      }
229
      // Should use the value type from the reference here due to the dynamic
230
      // typing rule of the null references.
231
0
      Returns[RTypes.size() - I - 1] = std::make_pair(Val, RefType);
232
0
    } else {
233
      // For the number type cases of the return values, the unused bits should
234
      // be erased due to the security issue.
235
0
      cleanNumericVal(Val, RType);
236
0
      Returns[RTypes.size() - I - 1] = std::make_pair(Val, RType);
237
0
    }
238
0
  }
239
240
  // After execution, the value stack size should be 0.
241
0
  assuming(StackMgr.size() == 0);
242
0
  return Returns;
243
0
}
244
245
/// Async invoke function. See "include/executor/executor.h".
246
Async<Expect<std::vector<std::pair<ValVariant, ValType>>>>
247
Executor::asyncInvoke(const Runtime::Instance::FunctionInstance *FuncInst,
248
                      Span<const ValVariant> Params,
249
0
                      Span<const ValType> ParamTypes) {
250
0
  Expect<std::vector<std::pair<ValVariant, ValType>>> (Executor::*FPtr)(
251
0
      const Runtime::Instance::FunctionInstance *, Span<const ValVariant>,
252
0
      Span<const ValType>) = &Executor::invoke;
253
0
  return {FPtr, *this, FuncInst, std::vector(Params.begin(), Params.end()),
254
0
          std::vector(ParamTypes.begin(), ParamTypes.end())};
255
0
}
256
257
/// Invoke component function. See "include/executor/executor.h".
258
Expect<std::vector<std::pair<ComponentValVariant, ComponentValType>>>
259
Executor::invoke(const Runtime::Instance::Component::FunctionInstance *FuncInst,
260
                 Span<const ComponentValVariant> Params,
261
0
                 Span<const ComponentValType> ParamTypes) {
262
0
  if (unlikely(FuncInst == nullptr)) {
263
0
    spdlog::error(ErrCode::Value::FuncNotFound);
264
0
    return Unexpect(ErrCode::Value::FuncNotFound);
265
0
  }
266
267
  // Matching arguments and function type.
268
  // TODO: COMPONENT - type matching.
269
0
  const auto &ExpectedFuncType = FuncInst->getFuncType();
270
0
  const size_t ExpectedArity = ExpectedFuncType.getParamList().size();
271
0
  if (Params.size() != ParamTypes.size() || ParamTypes.size() < ExpectedArity) {
272
0
    spdlog::error(ErrCode::Value::FuncSigMismatch);
273
0
    spdlog::error("    expected {} argument(s), got {}"sv, ExpectedArity,
274
0
                  ParamTypes.size());
275
0
    return Unexpect(ErrCode::Value::FuncSigMismatch);
276
0
  }
277
278
  // Convert the component params into core WASM params.
279
0
  auto *ReallocFuncInst = FuncInst->getAllocFunction();
280
0
  auto *MemInst = FuncInst->getMemoryInstance();
281
0
  EXPECTED_TRY(auto CoreWASMArgs,
282
0
               convValsToCoreWASM(Params, ParamTypes, ReallocFuncInst, MemInst,
283
0
                                  FuncInst->getComponentInstance(),
284
0
                                  FuncInst->getStringEncoding()));
285
286
  // Call runFunction.
287
0
  auto *CoreFuncInst = FuncInst->getLowerFunction();
288
0
  assuming(CoreFuncInst);
289
0
  const auto &CoreFuncType = CoreFuncInst->getFuncType();
290
  // TODO: COMPONENT - check the ABI types between core functype and args.
291
0
  EXPECTED_TRY(auto CoreWASMReturns, invoke(CoreFuncInst, CoreWASMArgs,
292
0
                                            CoreFuncType.getParamTypes()));
293
294
  // Get return values.
295
0
  std::vector<ComponentValType> ReturnTypes;
296
0
  for (const auto &Type : FuncInst->getFuncType().getResultList()) {
297
0
    ReturnTypes.push_back(Type.getValType());
298
0
  }
299
0
  EXPECTED_TRY(auto Returns,
300
0
               convValsToComponent(CoreWASMReturns, ReturnTypes, MemInst,
301
0
                                   FuncInst->getComponentInstance(),
302
0
                                   FuncInst->getStringEncoding()));
303
0
  assuming(Returns.size() == ReturnTypes.size());
304
305
  // CanonicalABI.md L3367-3372: after a sync lift completes (post
306
  // task.return_), invoke the optional post-return with the ORIGINAL flat
307
  // core return values as parameters. This is how Preview 2 components free
308
  // buffers allocated for indirect-result / list / string returns.
309
  //
310
  // TODO: spec L3370 also gates this region with `may_leave = False`;
311
  // WasmEdge doesn't model may_leave yet (deferred along with async).
312
  // In practice sync Preview 2 post-return implementations don't re-enter.
313
0
  if (auto *PostReturnInst = FuncInst->getPostReturnFunction()) {
314
0
    std::vector<ValVariant> PRArgs;
315
0
    PRArgs.reserve(CoreWASMReturns.size());
316
0
    for (const auto &P : CoreWASMReturns) {
317
0
      PRArgs.push_back(P.first);
318
0
    }
319
0
    EXPECTED_TRY(invoke(PostReturnInst, PRArgs,
320
0
                        PostReturnInst->getFuncType().getParamTypes()));
321
0
  }
322
323
0
  return Returns;
324
0
}
325
326
} // namespace Executor
327
} // namespace WasmEdge