/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 "system/stacktrace.h" |
9 | | |
10 | | using namespace std::literals; |
11 | | |
12 | | namespace WasmEdge { |
13 | | namespace Executor { |
14 | | |
15 | | /// Instantiate a WASM Module. See "include/executor/executor.h". |
16 | | Expect<std::unique_ptr<Runtime::Instance::ModuleInstance>> |
17 | | Executor::instantiateModule(Runtime::StoreManager &StoreMgr, |
18 | 0 | const AST::Module &Mod) { |
19 | 0 | return instantiate(StoreMgr, Mod).map_error([this](auto E) { |
20 | | // If statistics are enabled, dump them here. |
21 | | // When an error occurs, subsequent execution will not run. |
22 | 0 | if (Stat) { |
23 | 0 | Stat->dumpToLog(Conf); |
24 | 0 | } |
25 | 0 | return E; |
26 | 0 | }); |
27 | 0 | } |
28 | | |
29 | | /// Register a named WASM module. See "include/executor/executor.h". |
30 | | Expect<std::unique_ptr<Runtime::Instance::ModuleInstance>> |
31 | | Executor::registerModule(Runtime::StoreManager &StoreMgr, |
32 | 0 | const AST::Module &Mod, std::string_view Name) { |
33 | 0 | return instantiate(StoreMgr, Mod, Name).map_error([this](auto E) { |
34 | | // If statistics are enabled, dump them here. |
35 | | // When an error occurs, subsequent execution will not run. |
36 | 0 | if (Stat) { |
37 | 0 | Stat->dumpToLog(Conf); |
38 | 0 | } |
39 | 0 | return E; |
40 | 0 | }); |
41 | 0 | } |
42 | | |
43 | | /// Register an instantiated module. See "include/executor/executor.h". |
44 | | Expect<void> |
45 | | Executor::registerModule(Runtime::StoreManager &StoreMgr, |
46 | 0 | const Runtime::Instance::ModuleInstance &ModInst) { |
47 | 0 | return StoreMgr.registerModule(&ModInst).map_error([](auto E) { |
48 | 0 | spdlog::error(E); |
49 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
50 | 0 | return E; |
51 | 0 | }); |
52 | 0 | } |
53 | | |
54 | | /// Register an instantiated module under an alias name. |
55 | | Expect<void> |
56 | | Executor::registerModule(Runtime::StoreManager &StoreMgr, |
57 | | const Runtime::Instance::ModuleInstance &ModInst, |
58 | 0 | std::string_view Name) { |
59 | 0 | return StoreMgr.registerModule(&ModInst, Name).map_error([](auto E) { |
60 | 0 | spdlog::error(E); |
61 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
62 | 0 | return E; |
63 | 0 | }); |
64 | 0 | } |
65 | | |
66 | | /// Instantiate a Component. See "include/executor/executor.h". |
67 | | Expect<std::unique_ptr<Runtime::Instance::ComponentInstance>> |
68 | | Executor::instantiateComponent(Runtime::StoreManager &StoreMgr, |
69 | 0 | const AST::Component::Component &Comp) { |
70 | 0 | return instantiate(StoreMgr, Comp); |
71 | 0 | } |
72 | | |
73 | | /// Register a named Component. See "include/executor/executor.h". |
74 | | Expect<std::unique_ptr<Runtime::Instance::ComponentInstance>> |
75 | | Executor::registerComponent(Runtime::StoreManager &StoreMgr, |
76 | | const AST::Component::Component &Comp, |
77 | 0 | std::string_view Name) { |
78 | 0 | return instantiate(StoreMgr, Comp, Name); |
79 | 0 | } |
80 | | |
81 | | /// Register an instantiated Component. See "include/executor/executor.h". |
82 | | Expect<void> Executor::registerComponent( |
83 | | Runtime::StoreManager &StoreMgr, |
84 | 0 | const Runtime::Instance::ComponentInstance &CompInst) { |
85 | 0 | return StoreMgr.registerComponent(&CompInst).map_error([](auto E) { |
86 | 0 | spdlog::error(E); |
87 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Component)); |
88 | 0 | return E; |
89 | 0 | }); |
90 | 0 | } |
91 | | |
92 | | /// Register a host function which will be invoked before calling a |
93 | | /// host function. |
94 | | Expect<void> Executor::registerPreHostFunction( |
95 | 0 | void *HostData = nullptr, std::function<void(void *)> HostFunc = nullptr) { |
96 | 0 | HostFuncHelper.setPreHost(HostData, HostFunc); |
97 | 0 | return {}; |
98 | 0 | } |
99 | | |
100 | | /// Register a host function which will be invoked after calling a |
101 | | /// host function. |
102 | | Expect<void> Executor::registerPostHostFunction( |
103 | 0 | void *HostData = nullptr, std::function<void(void *)> HostFunc = nullptr) { |
104 | 0 | HostFuncHelper.setPostHost(HostData, HostFunc); |
105 | 0 | return {}; |
106 | 0 | } |
107 | | |
108 | | /// Invoke function. See "include/executor/executor.h". |
109 | | Expect<std::vector<std::pair<ValVariant, ValType>>> |
110 | | Executor::invoke(const Runtime::Instance::FunctionInstance *FuncInst, |
111 | | Span<const ValVariant> Params, |
112 | 0 | Span<const ValType> ParamTypes) { |
113 | 0 | if (unlikely(FuncInst == nullptr)) { |
114 | 0 | spdlog::error(ErrCode::Value::FuncNotFound); |
115 | 0 | return Unexpect(ErrCode::Value::FuncNotFound); |
116 | 0 | } |
117 | | |
118 | | // Matching arguments and function type. |
119 | 0 | const auto &FuncType = FuncInst->getFuncType(); |
120 | 0 | const auto &PTypes = FuncType.getParamTypes(); |
121 | 0 | const auto &RTypes = FuncType.getReturnTypes(); |
122 | | // The defined type list may be empty if the function is an independent |
123 | | // function instance, that is, the module instance will be nullptr. In this |
124 | | // case, all value types are number types or abstract heap types. |
125 | | // |
126 | | // If a function belongs to a component instance, its type should already be |
127 | | // converted, so the type list is not needed. |
128 | 0 | WasmEdge::Span<const WasmEdge::AST::SubType *const> TypeList = {}; |
129 | 0 | if (FuncInst->getModule()) { |
130 | 0 | TypeList = FuncInst->getModule()->getTypeList(); |
131 | 0 | } |
132 | 0 | if (!AST::TypeMatcher::matchTypes(TypeList, ParamTypes, PTypes)) { |
133 | 0 | spdlog::error(ErrCode::Value::FuncSigMismatch); |
134 | 0 | spdlog::error(ErrInfo::InfoMismatch( |
135 | 0 | PTypes, RTypes, std::vector(ParamTypes.begin(), ParamTypes.end()), |
136 | 0 | RTypes)); |
137 | 0 | return Unexpect(ErrCode::Value::FuncSigMismatch); |
138 | 0 | } |
139 | | |
140 | | // Check the reference value validation. |
141 | 0 | for (uint32_t I = 0; I < ParamTypes.size(); ++I) { |
142 | 0 | if (ParamTypes[I].isRefType() && (!ParamTypes[I].isNullableRefType() && |
143 | 0 | Params[I].get<RefVariant>().isNull())) { |
144 | 0 | spdlog::error(ErrCode::Value::NonNullRequired); |
145 | 0 | spdlog::error(" Cannot pass a null reference as argument of {}."sv, |
146 | 0 | ParamTypes[I]); |
147 | 0 | return Unexpect(ErrCode::Value::NonNullRequired); |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | 0 | Runtime::StackManager StackMgr; |
152 | | |
153 | | // Call runFunction. |
154 | 0 | EXPECTED_TRY(runFunction(StackMgr, *FuncInst, Params).map_error([](auto E) { |
155 | 0 | if (E != ErrCode::Value::Terminated) { |
156 | 0 | dumpStackTrace(Span<const uint32_t>{StackTrace}.first(StackTraceSize)); |
157 | 0 | } |
158 | 0 | return E; |
159 | 0 | })); |
160 | | |
161 | | // Get return values. |
162 | 0 | std::vector<std::pair<ValVariant, ValType>> Returns(RTypes.size()); |
163 | 0 | for (uint32_t I = 0; I < RTypes.size(); ++I) { |
164 | 0 | auto Val = StackMgr.pop(); |
165 | 0 | const auto &RType = RTypes[RTypes.size() - I - 1]; |
166 | 0 | if (RType.isRefType()) { |
167 | | // For the reference type cases of the return values, they should be |
168 | | // transformed into abstract heap types due to the opaque of type indices. |
169 | 0 | auto &RefType = Val.get<RefVariant>().getType(); |
170 | 0 | if (RefType.isExternalized()) { |
171 | | // First handle the forced externalized value type case. |
172 | 0 | RefType = ValType(TypeCode::Ref, TypeCode::ExternRef); |
173 | 0 | } |
174 | 0 | if (!RefType.isAbsHeapType()) { |
175 | | // The instance must not be nullptr because the null references are |
176 | | // already dynamic typed into the top abstract heap type. |
177 | 0 | auto *Inst = |
178 | 0 | Val.get<RefVariant>().getPtr<Runtime::Instance::CompositeBase>(); |
179 | 0 | assuming(Inst); |
180 | | // The ModInst may be nullptr only in the independent host function |
181 | | // instance. Therefore the module instance here must not be nullptr |
182 | | // because the independent host function instance cannot be imported and |
183 | | // be referred by instructions. |
184 | 0 | const auto *ModInst = Inst->getModule(); |
185 | 0 | const auto *DefType = *ModInst->getType(RefType.getTypeIndex()); |
186 | 0 | RefType = |
187 | 0 | ValType(RefType.getCode(), DefType->getCompositeType().expand()); |
188 | 0 | } |
189 | | // Should use the value type from the reference here due to the dynamic |
190 | | // typing rule of the null references. |
191 | 0 | Returns[RTypes.size() - I - 1] = std::make_pair(Val, RefType); |
192 | 0 | } else { |
193 | | // For the number type cases of the return values, the unused bits should |
194 | | // be erased due to the security issue. |
195 | 0 | cleanNumericVal(Val, RType); |
196 | 0 | Returns[RTypes.size() - I - 1] = std::make_pair(Val, RType); |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | // After execution, the value stack size should be 0. |
201 | 0 | assuming(StackMgr.size() == 0); |
202 | 0 | return Returns; |
203 | 0 | } |
204 | | |
205 | | /// Async invoke function. See "include/executor/executor.h". |
206 | | Async<Expect<std::vector<std::pair<ValVariant, ValType>>>> |
207 | | Executor::asyncInvoke(const Runtime::Instance::FunctionInstance *FuncInst, |
208 | | Span<const ValVariant> Params, |
209 | 0 | Span<const ValType> ParamTypes) { |
210 | 0 | Expect<std::vector<std::pair<ValVariant, ValType>>> (Executor::*FPtr)( |
211 | 0 | const Runtime::Instance::FunctionInstance *, Span<const ValVariant>, |
212 | 0 | Span<const ValType>) = &Executor::invoke; |
213 | 0 | return {FPtr, *this, FuncInst, std::vector(Params.begin(), Params.end()), |
214 | 0 | std::vector(ParamTypes.begin(), ParamTypes.end())}; |
215 | 0 | } |
216 | | |
217 | | /// Invoke component function. See "include/executor/executor.h". |
218 | | Expect<std::vector<std::pair<ComponentValVariant, ComponentValType>>> |
219 | | Executor::invoke(const Runtime::Instance::Component::FunctionInstance *FuncInst, |
220 | | Span<const ComponentValVariant> Params, |
221 | 0 | Span<const ComponentValType> ParamTypes) { |
222 | 0 | if (unlikely(FuncInst == nullptr)) { |
223 | 0 | spdlog::error(ErrCode::Value::FuncNotFound); |
224 | 0 | return Unexpect(ErrCode::Value::FuncNotFound); |
225 | 0 | } |
226 | | |
227 | | // Matching arguments and function type. |
228 | | // TODO: COMPONENT - type matching. |
229 | | // const auto &FuncType = FuncInst->getFuncType(); |
230 | | // const auto PTypes = FuncType.getParamList(); |
231 | 0 | const auto &ExpectedFuncType = FuncInst->getFuncType(); |
232 | 0 | const size_t ExpectedArity = ExpectedFuncType.getParamList().size(); |
233 | 0 | if (Params.size() != ParamTypes.size() || ParamTypes.size() < ExpectedArity) { |
234 | 0 | spdlog::error(ErrCode::Value::FuncSigMismatch); |
235 | 0 | spdlog::error(" expected {} argument(s), got {}"sv, ExpectedArity, |
236 | 0 | ParamTypes.size()); |
237 | 0 | return Unexpect(ErrCode::Value::FuncSigMismatch); |
238 | 0 | } |
239 | | |
240 | | // Convert the component params into core WASM params. |
241 | 0 | auto *ReallocFuncInst = FuncInst->getAllocFunction(); |
242 | 0 | auto *MemInst = FuncInst->getMemoryInstance(); |
243 | 0 | std::vector<ValVariant> CoreWASMArgs = |
244 | 0 | convValsToCoreWASM(Params, ParamTypes, ReallocFuncInst, MemInst); |
245 | | |
246 | | // Call runFunction. |
247 | 0 | auto *CoreFuncInst = FuncInst->getLowerFunction(); |
248 | 0 | assuming(CoreFuncInst); |
249 | 0 | const auto &CoreFuncType = CoreFuncInst->getFuncType(); |
250 | | // TODO: COMPONENT - check the ABI types between core functype and args. |
251 | 0 | EXPECTED_TRY(auto CoreWASMReturns, invoke(CoreFuncInst, CoreWASMArgs, |
252 | 0 | CoreFuncType.getParamTypes())); |
253 | | |
254 | | // Get return values. |
255 | 0 | std::vector<ComponentValType> ReturnTypes; |
256 | 0 | for (const auto &Type : FuncInst->getFuncType().getResultList()) { |
257 | 0 | ReturnTypes.push_back(Type.getValType()); |
258 | 0 | } |
259 | 0 | EXPECTED_TRY(auto Returns, |
260 | 0 | convValsToComponent(CoreWASMReturns, ReturnTypes, MemInst)); |
261 | 0 | assuming(Returns.size() == ReturnTypes.size()); |
262 | 0 | return Returns; |
263 | 0 | } |
264 | | |
265 | | } // namespace Executor |
266 | | } // namespace WasmEdge |