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