Coverage Report

Created: 2025-09-27 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/executor/instantiate/import.cpp
Line
Count
Source
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
9
#include <cstdint>
10
#include <string_view>
11
#include <utility>
12
13
using namespace std::literals;
14
15
namespace WasmEdge {
16
namespace Executor {
17
18
namespace {
19
template <typename... Args>
20
auto logMatchError(std::string_view ModName, std::string_view ExtName,
21
0
                   ExternalType ExtType, Args &&...Values) {
22
0
  spdlog::error(ErrCode::Value::IncompatibleImportType);
23
0
  spdlog::error(ErrInfo::InfoMismatch(std::forward<Args>(Values)...));
24
0
  spdlog::error(ErrInfo::InfoLinking(ModName, ExtName, ExtType));
25
0
  spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Desc_Import));
26
0
  return Unexpect(ErrCode::Value::IncompatibleImportType);
27
0
}
Unexecuted instantiation: import.cpp:auto WasmEdge::Executor::(anonymous namespace)::logMatchError<WasmEdge::ExternalType const&, WasmEdge::ExternalType>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, WasmEdge::ExternalType, WasmEdge::ExternalType const&, WasmEdge::ExternalType&&)
Unexecuted instantiation: import.cpp:auto WasmEdge::Executor::(anonymous namespace)::logMatchError<std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, WasmEdge::ExternalType, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&)
Unexecuted instantiation: import.cpp:auto WasmEdge::Executor::(anonymous namespace)::logMatchError<WasmEdge::ValType const&, bool, unsigned int, unsigned int, WasmEdge::ValType const&, bool, unsigned int, unsigned int>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, WasmEdge::ExternalType, WasmEdge::ValType const&, bool&&, unsigned int&&, unsigned int&&, WasmEdge::ValType const&, bool&&, unsigned int&&, unsigned int&&)
Unexecuted instantiation: import.cpp:auto WasmEdge::Executor::(anonymous namespace)::logMatchError<bool, unsigned int, unsigned int, bool, unsigned int, unsigned int>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, WasmEdge::ExternalType, bool&&, unsigned int&&, unsigned int&&, bool&&, unsigned int&&, unsigned int&&)
Unexecuted instantiation: import.cpp:auto WasmEdge::Executor::(anonymous namespace)::logMatchError<WasmEdge::ValType const&, WasmEdge::ValMut, WasmEdge::ValType const&, WasmEdge::ValMut>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, WasmEdge::ExternalType, WasmEdge::ValType const&, WasmEdge::ValMut&&, WasmEdge::ValType const&, WasmEdge::ValMut&&)
28
29
auto logUnknownError(std::string_view ModName, std::string_view ExtName,
30
0
                     ExternalType ExtType) {
31
0
  spdlog::error(ErrCode::Value::UnknownImport);
32
0
  spdlog::error(ErrInfo::InfoLinking(ModName, ExtName, ExtType));
33
0
  spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Desc_Import));
34
0
  return Unexpect(ErrCode::Value::UnknownImport);
35
0
}
36
37
0
bool matchLimit(const AST::Limit &Exp, const AST::Limit &Got) {
38
0
  if (Exp.isShared() != Got.isShared()) {
39
0
    return false;
40
0
  }
41
0
  if ((Got.getMin() < Exp.getMin()) || (Exp.hasMax() && !Got.hasMax())) {
42
0
    return false;
43
0
  }
44
0
  if (Exp.hasMax() && Got.hasMax() && Got.getMax() > Exp.getMax()) {
45
0
    return false;
46
0
  }
47
0
  return true;
48
0
}
49
50
Expect<void>
51
checkImportMatched(std::string_view ModName, std::string_view ExtName,
52
                   const ExternalType ExtType,
53
0
                   const Runtime::Instance::ModuleInstance &ModInst) {
54
0
  switch (ExtType) {
55
0
  case ExternalType::Function:
56
0
    if (auto Res = ModInst.findFuncExports(ExtName); likely(Res != nullptr)) {
57
0
      return {};
58
0
    }
59
0
    break;
60
0
  case ExternalType::Table:
61
0
    if (auto Res = ModInst.findTableExports(ExtName); likely(Res != nullptr)) {
62
0
      return {};
63
0
    }
64
0
    break;
65
0
  case ExternalType::Memory:
66
0
    if (auto Res = ModInst.findMemoryExports(ExtName); likely(Res != nullptr)) {
67
0
      return {};
68
0
    }
69
0
    break;
70
0
  case ExternalType::Global:
71
0
    if (auto Res = ModInst.findGlobalExports(ExtName); likely(Res != nullptr)) {
72
0
      return {};
73
0
    }
74
0
    break;
75
0
  case ExternalType::Tag:
76
0
    if (auto Res = ModInst.findTagExports(ExtName); likely(Res != nullptr)) {
77
0
      return {};
78
0
    }
79
0
    break;
80
0
  default:
81
0
    return logUnknownError(ModName, ExtName, ExtType);
82
0
  }
83
84
  // Check is error external type or unknown imports.
85
0
  if (ModInst.findFuncExports(ExtName)) {
86
0
    return logMatchError(ModName, ExtName, ExtType, ExtType,
87
0
                         ExternalType::Function);
88
0
  }
89
0
  if (ModInst.findTableExports(ExtName)) {
90
0
    return logMatchError(ModName, ExtName, ExtType, ExtType,
91
0
                         ExternalType::Table);
92
0
  }
93
0
  if (ModInst.findMemoryExports(ExtName)) {
94
0
    return logMatchError(ModName, ExtName, ExtType, ExtType,
95
0
                         ExternalType::Memory);
96
0
  }
97
0
  if (ModInst.findTagExports(ExtName)) {
98
0
    return logMatchError(ModName, ExtName, ExtType, ExtType, ExternalType::Tag);
99
0
  }
100
0
  if (ModInst.findGlobalExports(ExtName)) {
101
0
    return logMatchError(ModName, ExtName, ExtType, ExtType,
102
0
                         ExternalType::Global);
103
0
  }
104
105
0
  return logUnknownError(ModName, ExtName, ExtType);
106
0
}
107
} // namespace
108
109
// Instantiate imports. See "include/executor/executor.h".
110
Expect<void> Executor::instantiate(
111
    std::function<const Runtime::Instance::ModuleInstance *(std::string_view)>
112
        ModuleFinder,
113
    Runtime::Instance::ModuleInstance &ModInst,
114
0
    const AST::ImportSection &ImportSec) {
115
  // Iterate and instantiate import descriptions.
116
0
  for (const auto &ImpDesc : ImportSec.getContent()) {
117
    // Get data from import description and find import module.
118
0
    auto ExtType = ImpDesc.getExternalType();
119
0
    auto ModName = ImpDesc.getModuleName();
120
0
    auto ExtName = ImpDesc.getExternalName();
121
0
    const auto *ImpModInst = ModuleFinder(ModName);
122
0
    if (unlikely(ImpModInst == nullptr)) {
123
0
      auto Res = logUnknownError(ModName, ExtName, ExtType);
124
0
      if (ModName == "wasi_snapshot_preview1"sv) {
125
0
        spdlog::error("    This is a WASI related import. Please ensure that "
126
0
                      "you've turned on the WASI configuration."sv);
127
0
      } else if (ModName == "wasi_nn"sv) {
128
0
        spdlog::error("    This is a WASI-NN related import. Please ensure "
129
0
                      "that you've turned on the WASI-NN configuration and "
130
0
                      "installed the WASI-NN plug-in."sv);
131
0
      } else if (ModName == "wasi_crypto_common"sv ||
132
0
                 ModName == "wasi_crypto_asymmetric_common"sv ||
133
0
                 ModName == "wasi_crypto_kx"sv ||
134
0
                 ModName == "wasi_crypto_signatures"sv ||
135
0
                 ModName == "wasi_crypto_symmetric"sv) {
136
0
        spdlog::error("    This is a WASI-Crypto related import. Please ensure "
137
0
                      "that you've turned on the WASI-Crypto configuration and "
138
0
                      "installed the WASI-Crypto plug-in.");
139
0
      } else if (ModName == "env"sv) {
140
0
        spdlog::error(
141
0
            "    This may be the import of host environment like JavaScript or "
142
0
            "Golang. Please check that you've registered the necessary host "
143
0
            "modules from the host programming language."sv);
144
0
      }
145
0
      return Res;
146
0
    }
147
0
    EXPECTED_TRY(checkImportMatched(ModName, ExtName, ExtType, *ImpModInst));
148
149
    // Add the imports into module instance.
150
0
    switch (ExtType) {
151
0
    case ExternalType::Function: {
152
      // Get function type index. External type checked in validation.
153
0
      uint32_t TypeIdx = ImpDesc.getExternalFuncTypeIdx();
154
      // Import matching.
155
0
      auto *ImpInst = ImpModInst->findFuncExports(ExtName);
156
      // External function type should match the import function type in
157
      // description.
158
159
0
      if (!AST::TypeMatcher::matchType(ModInst.getTypeList(), TypeIdx,
160
0
                                       ImpModInst->getTypeList(),
161
0
                                       ImpInst->getTypeIndex())) {
162
0
        const auto &ExpDefType = **ModInst.getType(TypeIdx);
163
0
        bool IsMatchV2 = false;
164
0
        const auto &ExpFuncType = ExpDefType.getCompositeType().getFuncType();
165
0
        const auto &ImpFuncType = ImpInst->getFuncType();
166
0
        if (ModName == "wasi_snapshot_preview1"sv) {
167
          /*
168
           * The following functions should provide V1 and V2.
169
             "sock_open_v2",
170
             "sock_bind_v2",
171
             "sock_connect_v2",
172
             "sock_listen_v2",
173
             "sock_accept_v2",
174
             "sock_recv_v2",
175
             "sock_recv_from_v2",
176
             "sock_send_v2",
177
             "sock_send_to_v2",
178
             "sock_getlocaladdr_v2",
179
             "sock_getpeeraddr_v2"
180
             */
181
0
          std::vector<std::string_view> CompatibleWASISocketAPI = {
182
0
              "sock_open"sv,         "sock_bind"sv,       "sock_connect"sv,
183
0
              "sock_listen"sv,       "sock_accept"sv,     "sock_recv"sv,
184
0
              "sock_recv_from"sv,    "sock_send"sv,       "sock_send_to"sv,
185
0
              "sock_getlocaladdr"sv, "sock_getpeeraddr"sv};
186
0
          for (auto Iter = CompatibleWASISocketAPI.begin();
187
0
               Iter != CompatibleWASISocketAPI.end(); Iter++) {
188
0
            if (ExtName == *Iter) {
189
0
              auto *ImpInstV2 =
190
0
                  ImpModInst->findFuncExports(std::string(*Iter) + "_v2");
191
0
              if (!AST::TypeMatcher::matchType(
192
0
                      ModInst.getTypeList(), *ExpDefType.getTypeIndex(),
193
0
                      ImpModInst->getTypeList(), ImpInst->getTypeIndex())) {
194
                // Try to match the new version
195
0
                ImpInst = ImpInstV2;
196
0
                IsMatchV2 = true;
197
0
                break;
198
0
              }
199
0
            }
200
0
          }
201
0
        }
202
0
        if (!IsMatchV2) {
203
0
          return logMatchError(
204
0
              ModName, ExtName, ExtType, ExpFuncType.getParamTypes(),
205
0
              ExpFuncType.getReturnTypes(), ImpFuncType.getParamTypes(),
206
0
              ImpFuncType.getReturnTypes());
207
0
        }
208
0
      }
209
      // Set the matched function address to module instance.
210
0
      ModInst.importFunction(ImpInst);
211
212
      // If the imported function is a WASI function, mark in the module.
213
0
      if (!ModInst.getWASIModule() && ModName == "wasi_snapshot_preview1"sv) {
214
0
        ModInst.setWASIModule(ImpModInst);
215
0
      }
216
0
      break;
217
0
    }
218
0
    case ExternalType::Table: {
219
      // Get table type. External type checked in validation.
220
0
      const auto &TabType = ImpDesc.getExternalTableType();
221
0
      const auto &TabLim = TabType.getLimit();
222
      // Import matching. External table type should match the one in import
223
      // description.
224
0
      auto *ImpInst = ImpModInst->findTableExports(ExtName);
225
0
      const auto &ImpType = ImpInst->getTableType();
226
0
      const auto &ImpLim = ImpType.getLimit();
227
      // External table reference type should match the import table reference
228
      // type in description, and vice versa.
229
0
      if (!AST::TypeMatcher::matchType(
230
0
              ModInst.getTypeList(), TabType.getRefType(),
231
0
              ImpModInst->getTypeList(), ImpType.getRefType()) ||
232
0
          !AST::TypeMatcher::matchType(
233
0
              ImpModInst->getTypeList(), ImpType.getRefType(),
234
0
              ModInst.getTypeList(), TabType.getRefType()) ||
235
0
          !matchLimit(TabLim, ImpLim)) {
236
0
        return logMatchError(ModName, ExtName, ExtType, TabType.getRefType(),
237
0
                             TabLim.hasMax(), TabLim.getMin(), TabLim.getMax(),
238
0
                             ImpType.getRefType(), ImpLim.hasMax(),
239
0
                             ImpLim.getMin(), ImpLim.getMax());
240
0
      }
241
      // Set the matched table address to module instance.
242
0
      ModInst.importTable(ImpInst);
243
0
      break;
244
0
    }
245
0
    case ExternalType::Memory: {
246
      // Get memory type. External type checked in validation.
247
0
      const auto &MemType = ImpDesc.getExternalMemoryType();
248
0
      const auto &MemLim = MemType.getLimit();
249
      // Import matching. External memory type should match the one in import
250
      // description.
251
0
      auto *ImpInst = ImpModInst->findMemoryExports(ExtName);
252
0
      const auto &ImpLim = ImpInst->getMemoryType().getLimit();
253
0
      if (!matchLimit(MemLim, ImpLim)) {
254
0
        return logMatchError(ModName, ExtName, ExtType, MemLim.hasMax(),
255
0
                             MemLim.getMin(), MemLim.getMax(), ImpLim.hasMax(),
256
0
                             ImpLim.getMin(), ImpLim.getMax());
257
0
      }
258
      // Set the matched memory address to module instance.
259
0
      ModInst.importMemory(ImpInst);
260
0
      break;
261
0
    }
262
0
    case ExternalType::Tag: {
263
      // Get tag type. External type checked in validation.
264
0
      const auto &TagType = ImpDesc.getExternalTagType();
265
      // Import matching.
266
0
      auto *ImpInst = ImpModInst->findTagExports(ExtName);
267
0
      if (!AST::TypeMatcher::matchType(
268
0
              ModInst.getTypeList(), TagType.getTypeIdx(),
269
0
              ImpModInst->getTypeList(), ImpInst->getTagType().getTypeIdx())) {
270
0
        const auto &ExpFuncType =
271
0
            TagType.getDefType().getCompositeType().getFuncType();
272
0
        const auto &ImpFuncType =
273
0
            ImpInst->getTagType().getDefType().getCompositeType().getFuncType();
274
0
        return logMatchError(
275
0
            ModName, ExtName, ExtType, ExpFuncType.getParamTypes(),
276
0
            ExpFuncType.getReturnTypes(), ImpFuncType.getParamTypes(),
277
0
            ImpFuncType.getReturnTypes());
278
0
      }
279
0
      ModInst.importTag(ImpInst);
280
0
      break;
281
0
    }
282
0
    case ExternalType::Global: {
283
      // Get global type. External type checked in validation.
284
0
      const auto &GlobType = ImpDesc.getExternalGlobalType();
285
      // Import matching. External global type should match the one in
286
      // import description.
287
0
      auto *ImpInst = ImpModInst->findGlobalExports(ExtName);
288
0
      const auto &ImpType = ImpInst->getGlobalType();
289
0
      bool IsMatch = false;
290
0
      if (ImpType.getValMut() == GlobType.getValMut()) {
291
        // For both const or both var: external global value type should match
292
        // the import global value type in description.
293
0
        IsMatch = AST::TypeMatcher::matchType(
294
0
            ModInst.getTypeList(), GlobType.getValType(),
295
0
            ImpModInst->getTypeList(), ImpType.getValType());
296
0
        if (ImpType.getValMut() == ValMut::Var) {
297
          // If both var: import global value type in description should also
298
          // match the external global value type.
299
0
          IsMatch &= AST::TypeMatcher::matchType(
300
0
              ImpModInst->getTypeList(), ImpType.getValType(),
301
0
              ModInst.getTypeList(), GlobType.getValType());
302
0
        }
303
0
      }
304
0
      if (!IsMatch) {
305
0
        return logMatchError(ModName, ExtName, ExtType, GlobType.getValType(),
306
0
                             GlobType.getValMut(), ImpType.getValType(),
307
0
                             ImpType.getValMut());
308
0
      }
309
      // Set the matched global address to module instance.
310
0
      ModInst.importGlobal(ImpInst);
311
0
      break;
312
0
    }
313
0
    default:
314
0
      break;
315
0
    }
316
0
  }
317
0
  return {};
318
0
}
319
320
} // namespace Executor
321
} // namespace WasmEdge