/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 |