/src/WasmEdge/lib/executor/executor.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: 2019-2024 Second State INC |
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 is enabled, then dump it here. |
21 | | // When there is an error happened, the following execution will not |
22 | | // execute. |
23 | 0 | if (Stat) { |
24 | 0 | Stat->dumpToLog(Conf); |
25 | 0 | } |
26 | 0 | return E; |
27 | 0 | }); |
28 | 0 | } |
29 | | |
30 | | /// Register a named WASM module. See "include/executor/executor.h". |
31 | | Expect<std::unique_ptr<Runtime::Instance::ModuleInstance>> |
32 | | Executor::registerModule(Runtime::StoreManager &StoreMgr, |
33 | 0 | const AST::Module &Mod, std::string_view Name) { |
34 | 0 | return instantiate(StoreMgr, Mod, Name).map_error([this](auto E) { |
35 | | // If Statistics is enabled, then dump it here. |
36 | | // When there is an error happened, the following execution will not |
37 | | // execute. |
38 | 0 | if (Stat) { |
39 | 0 | Stat->dumpToLog(Conf); |
40 | 0 | } |
41 | 0 | return E; |
42 | 0 | }); |
43 | 0 | } |
44 | | |
45 | | /// Register an instantiated module. See "include/executor/executor.h". |
46 | | Expect<void> |
47 | | Executor::registerModule(Runtime::StoreManager &StoreMgr, |
48 | 0 | const Runtime::Instance::ModuleInstance &ModInst) { |
49 | 0 | return StoreMgr.registerModule(&ModInst).map_error([](auto E) { |
50 | 0 | E = ErrCode::Value::ModuleNameConflict; |
51 | 0 | spdlog::error(E); |
52 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
53 | 0 | return E; |
54 | 0 | }); |
55 | 0 | } |
56 | | |
57 | | /// Instantiate a Component. See "include/executor/executor.h". |
58 | | Expect<std::unique_ptr<Runtime::Instance::ComponentInstance>> |
59 | | Executor::instantiateComponent(Runtime::StoreManager &StoreMgr, |
60 | 0 | const AST::Component::Component &Comp) { |
61 | 0 | return instantiate(StoreMgr, Comp); |
62 | 0 | } |
63 | | |
64 | | /// Register a named Component. See "include/executor/executor.h". |
65 | | Expect<std::unique_ptr<Runtime::Instance::ComponentInstance>> |
66 | | Executor::registerComponent(Runtime::StoreManager &StoreMgr, |
67 | | const AST::Component::Component &Comp, |
68 | 0 | std::string_view Name) { |
69 | 0 | return instantiate(StoreMgr, Comp, Name); |
70 | 0 | } |
71 | | |
72 | | /// Register an instantiated Component. See "include/executor/executor.h". |
73 | | Expect<void> Executor::registerComponent( |
74 | | Runtime::StoreManager &StoreMgr, |
75 | 0 | const Runtime::Instance::ComponentInstance &CompInst) { |
76 | 0 | return StoreMgr.registerComponent(&CompInst).map_error([](auto E) { |
77 | 0 | E = ErrCode::Value::ModuleNameConflict; |
78 | 0 | spdlog::error(E); |
79 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Component)); |
80 | 0 | return E; |
81 | 0 | }); |
82 | 0 | } |
83 | | |
84 | | /// Register a host function which will be invoked before calling a |
85 | | /// host function. |
86 | | Expect<void> Executor::registerPreHostFunction( |
87 | 0 | void *HostData = nullptr, std::function<void(void *)> HostFunc = nullptr) { |
88 | 0 | HostFuncHelper.setPreHost(HostData, HostFunc); |
89 | 0 | return {}; |
90 | 0 | } |
91 | | |
92 | | /// Register a host function which will be invoked after calling a |
93 | | /// host function. |
94 | | Expect<void> Executor::registerPostHostFunction( |
95 | 0 | void *HostData = nullptr, std::function<void(void *)> HostFunc = nullptr) { |
96 | 0 | HostFuncHelper.setPostHost(HostData, HostFunc); |
97 | 0 | return {}; |
98 | 0 | } |
99 | | |
100 | | /// Invoke function. See "include/executor/executor.h". |
101 | | Expect<std::vector<std::pair<ValVariant, ValType>>> |
102 | | Executor::invoke(const Runtime::Instance::FunctionInstance *FuncInst, |
103 | | Span<const ValVariant> Params, |
104 | 0 | Span<const ValType> ParamTypes) { |
105 | 0 | if (unlikely(FuncInst == nullptr)) { |
106 | 0 | spdlog::error(ErrCode::Value::FuncNotFound); |
107 | 0 | return Unexpect(ErrCode::Value::FuncNotFound); |
108 | 0 | } |
109 | | |
110 | | // Matching arguments and function type. |
111 | 0 | const auto &FuncType = FuncInst->getFuncType(); |
112 | 0 | const auto &PTypes = FuncType.getParamTypes(); |
113 | 0 | const auto &RTypes = FuncType.getReturnTypes(); |
114 | | // The defined type list may be empty if the function is an independent |
115 | | // function instance, that is, the module instance will be nullptr. For this |
116 | | // case, all of value types are number types or abstract heap types. |
117 | | // |
118 | | // If a function belongs to component instance, we should totally get |
119 | | // converted type, so should no need type list. |
120 | 0 | WasmEdge::Span<const WasmEdge::AST::SubType *const> TypeList = {}; |
121 | 0 | if (FuncInst->getModule()) { |
122 | 0 | TypeList = FuncInst->getModule()->getTypeList(); |
123 | 0 | } |
124 | 0 | if (!AST::TypeMatcher::matchTypes(TypeList, ParamTypes, PTypes)) { |
125 | 0 | spdlog::error(ErrCode::Value::FuncSigMismatch); |
126 | 0 | spdlog::error(ErrInfo::InfoMismatch( |
127 | 0 | PTypes, RTypes, std::vector(ParamTypes.begin(), ParamTypes.end()), |
128 | 0 | RTypes)); |
129 | 0 | return Unexpect(ErrCode::Value::FuncSigMismatch); |
130 | 0 | } |
131 | | |
132 | | // Check the reference value validation. |
133 | 0 | for (uint32_t I = 0; I < ParamTypes.size(); ++I) { |
134 | 0 | if (ParamTypes[I].isRefType() && (!ParamTypes[I].isNullableRefType() && |
135 | 0 | Params[I].get<RefVariant>().isNull())) { |
136 | 0 | spdlog::error(ErrCode::Value::NonNullRequired); |
137 | 0 | spdlog::error(" Cannot pass a null reference as argument of {}."sv, |
138 | 0 | ParamTypes[I]); |
139 | 0 | return Unexpect(ErrCode::Value::NonNullRequired); |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | 0 | Runtime::StackManager StackMgr; |
144 | | |
145 | | // Call runFunction. |
146 | 0 | EXPECTED_TRY(runFunction(StackMgr, *FuncInst, Params).map_error([](auto E) { |
147 | 0 | if (E != ErrCode::Value::Terminated) { |
148 | 0 | dumpStackTrace(Span<const uint32_t>{StackTrace}.first(StackTraceSize)); |
149 | 0 | } |
150 | 0 | return E; |
151 | 0 | })); |
152 | | |
153 | | // Get return values. |
154 | 0 | std::vector<std::pair<ValVariant, ValType>> Returns(RTypes.size()); |
155 | 0 | for (uint32_t I = 0; I < RTypes.size(); ++I) { |
156 | 0 | auto Val = StackMgr.pop(); |
157 | 0 | const auto &RType = RTypes[RTypes.size() - I - 1]; |
158 | 0 | if (RType.isRefType()) { |
159 | | // For the reference type cases of the return values, they should be |
160 | | // transformed into abstract heap types due to the opaque of type indices. |
161 | 0 | auto &RefType = Val.get<RefVariant>().getType(); |
162 | 0 | if (RefType.isExternalized()) { |
163 | | // First handle the forced externalized value type case. |
164 | 0 | RefType = ValType(TypeCode::Ref, TypeCode::ExternRef); |
165 | 0 | } |
166 | 0 | if (!RefType.isAbsHeapType()) { |
167 | | // The instance must not be nullptr because the null references are |
168 | | // already dynamic typed into the top abstract heap type. |
169 | 0 | auto *Inst = |
170 | 0 | Val.get<RefVariant>().getPtr<Runtime::Instance::CompositeBase>(); |
171 | 0 | assuming(Inst); |
172 | | // The ModInst may be nullptr only in the independent host function |
173 | | // instance. Therefore the module instance here must not be nullptr |
174 | | // because the independent host function instance cannot be imported and |
175 | | // be referred by instructions. |
176 | 0 | const auto *ModInst = Inst->getModule(); |
177 | 0 | auto *DefType = *ModInst->getType(RefType.getTypeIndex()); |
178 | 0 | RefType = |
179 | 0 | ValType(RefType.getCode(), DefType->getCompositeType().expand()); |
180 | 0 | } |
181 | | // Should use the value type from the reference here due to the dynamic |
182 | | // typing rule of the null references. |
183 | 0 | Returns[RTypes.size() - I - 1] = std::make_pair(Val, RefType); |
184 | 0 | } else { |
185 | | // For the number type cases of the return values, the unused bits should |
186 | | // be erased due to the security issue. |
187 | 0 | cleanNumericVal(Val, RType); |
188 | 0 | Returns[RTypes.size() - I - 1] = std::make_pair(Val, RType); |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | // After execution, the value stack size should be 0. |
193 | 0 | assuming(StackMgr.size() == 0); |
194 | 0 | return Returns; |
195 | 0 | } |
196 | | |
197 | | /// Async invoke function. See "include/executor/executor.h". |
198 | | Async<Expect<std::vector<std::pair<ValVariant, ValType>>>> |
199 | | Executor::asyncInvoke(const Runtime::Instance::FunctionInstance *FuncInst, |
200 | | Span<const ValVariant> Params, |
201 | 0 | Span<const ValType> ParamTypes) { |
202 | 0 | Expect<std::vector<std::pair<ValVariant, ValType>>> (Executor::*FPtr)( |
203 | 0 | const Runtime::Instance::FunctionInstance *, Span<const ValVariant>, |
204 | 0 | Span<const ValType>) = &Executor::invoke; |
205 | 0 | return {FPtr, *this, FuncInst, std::vector(Params.begin(), Params.end()), |
206 | 0 | std::vector(ParamTypes.begin(), ParamTypes.end())}; |
207 | 0 | } |
208 | | |
209 | | /// Invoke component function. See "include/executor/executor.h". |
210 | | Expect<std::vector<std::pair<ValInterface, ValType>>> |
211 | | Executor::invoke(const Runtime::Instance::Component::FunctionInstance *FuncInst, |
212 | | Span<const ValInterface> Params, |
213 | 0 | Span<const ValType> ParamTypes) { |
214 | | // NOTE: due to internal reason, we accept the multi-return values, but in |
215 | | // fact a component function will only return at most one. This concept |
216 | | // mismatching should be fix in the future. |
217 | 0 | if (unlikely(FuncInst == nullptr)) { |
218 | 0 | spdlog::error(ErrCode::Value::FuncNotFound); |
219 | 0 | return Unexpect(ErrCode::Value::FuncNotFound); |
220 | 0 | } |
221 | | |
222 | | // Matching arguments and function type. |
223 | 0 | const auto &FuncType = FuncInst->getFuncType(); |
224 | 0 | const auto &PTypes = FuncType.getParamTypes(); |
225 | 0 | const auto &RTypes = FuncType.getReturnTypes(); |
226 | |
|
227 | 0 | Span<const WasmEdge::AST::SubType *const> TypeList = {}; |
228 | 0 | if (!AST::TypeMatcher::matchTypes(TypeList, ParamTypes, PTypes)) { |
229 | 0 | spdlog::error(ErrCode::Value::FuncSigMismatch); |
230 | 0 | spdlog::error(ErrInfo::InfoMismatch( |
231 | 0 | PTypes, RTypes, std::vector(ParamTypes.begin(), ParamTypes.end()), |
232 | 0 | RTypes)); |
233 | 0 | return Unexpect(ErrCode::Value::FuncSigMismatch); |
234 | 0 | } |
235 | | |
236 | 0 | auto &HostFunc = FuncInst->getHostFunc(); |
237 | 0 | std::vector<ValInterface> Rets(RTypes.size()); |
238 | |
|
239 | 0 | EXPECTED_TRY(HostFunc.run(std::move(Params), Rets)); |
240 | | |
241 | 0 | std::vector<std::pair<ValInterface, ValType>> R; |
242 | 0 | auto RType = RTypes.begin(); |
243 | 0 | for (auto &&V : Rets) { |
244 | 0 | R.push_back(std::pair(V, *RType)); |
245 | 0 | RType++; |
246 | 0 | } |
247 | 0 | return R; |
248 | 0 | } |
249 | | |
250 | | } // namespace Executor |
251 | | } // namespace WasmEdge |