Coverage Report

Created: 2026-06-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/plugin/plugin.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "plugin/plugin.h"
5
#include "common/errcode.h"
6
#include "common/version.h"
7
#include "wasmedge/wasmedge.h"
8
9
// BUILTIN-PLUGIN: Headers for built-in plug-ins.
10
#include "plugin/wasi_logging/module.h"
11
12
#include <string>
13
#include <type_traits>
14
#include <unordered_set>
15
#include <variant>
16
17
#if WASMEDGE_OS_LINUX || WASMEDGE_OS_MACOS
18
#include <dlfcn.h>
19
#include <pwd.h>
20
#include <unistd.h>
21
#elif WASMEDGE_OS_WINDOWS
22
#include "system/winapi.h"
23
24
static bool GetFunctionModuleFileName(void *FuncPtr,
25
                                      std::filesystem::path &Path) {
26
  WasmEdge::winapi::HMODULE_ Module = nullptr;
27
28
  if (!WasmEdge::winapi::GetModuleHandleExW(
29
          WasmEdge::winapi::GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS_ |
30
              WasmEdge::winapi::GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT_,
31
          reinterpret_cast<WasmEdge::winapi::LPCWSTR_>(FuncPtr), &Module)) {
32
    return false;
33
  }
34
35
  std::vector<wchar_t> Buffer;
36
  WasmEdge::winapi::DWORD_ CopiedSize;
37
  do {
38
    Buffer.resize(Buffer.size() + WasmEdge::winapi::MAX_PATH_);
39
    CopiedSize = WasmEdge::winapi::GetModuleFileNameW(
40
        Module, Buffer.data(),
41
        static_cast<WasmEdge::winapi::DWORD_>(Buffer.size()));
42
    if (CopiedSize == 0) {
43
      return false;
44
    }
45
  } while (CopiedSize >= Buffer.size());
46
47
  Path.assign(std::wstring_view(Buffer.data(), CopiedSize));
48
  return true;
49
}
50
#endif
51
52
namespace WasmEdge {
53
54
namespace PO {
55
template <> struct Parser<WasmEdge_String> {
56
  static cxx20::expected<WasmEdge_String, Error>
57
0
  parse(std::string Value) noexcept {
58
0
    if (!Value.empty()) {
59
0
      const uint32_t Length = static_cast<uint32_t>(Value.size());
60
0
      char *Buf = new char[Value.size()];
61
0
      std::copy_n(Value.data(), Value.size(), Buf);
62
0
      return WasmEdge_String{/* Length */ Length, /* Buf */ Buf};
63
0
    }
64
0
    return WasmEdge_String{/* Length */ 0, /* Buf */ nullptr};
65
0
  }
66
};
67
} // namespace PO
68
69
namespace Plugin {
70
71
namespace {
72
73
class CAPIPluginRegister {
74
public:
75
  CAPIPluginRegister(const CAPIPluginRegister &) = delete;
76
  CAPIPluginRegister &operator=(const CAPIPluginRegister &) = delete;
77
78
0
  ~CAPIPluginRegister() noexcept {
79
    // Remove this register's entries from the static lookup so that a discarded
80
    // register (failed registration) leaves no dangling keys behind.
81
0
    for (const auto &ModuleDesc : ModuleDescriptions) {
82
0
      DescriptionLookup.erase(&ModuleDesc);
83
0
    }
84
0
  }
85
86
0
  CAPIPluginRegister(const WasmEdge_PluginDescriptor *Desc) noexcept {
87
0
    ModuleDescriptions.resize(Desc->ModuleCount);
88
0
    for (size_t I = 0; I < ModuleDescriptions.size(); ++I) {
89
0
      ModuleDescriptions[I].Name = Desc->ModuleDescriptions[I].Name;
90
0
      ModuleDescriptions[I].Description =
91
0
          Desc->ModuleDescriptions[I].Description;
92
0
      ModuleDescriptions[I].Create = &createWrapper;
93
0
      DescriptionLookup.emplace(&ModuleDescriptions[I],
94
0
                                &Desc->ModuleDescriptions[I]);
95
0
    }
96
97
0
    Descriptor.Name = Desc->Name;
98
0
    Descriptor.Description = Desc->Description;
99
0
    Descriptor.APIVersion = Desc->APIVersion;
100
0
    Descriptor.Version.Major = Desc->Version.Major;
101
0
    Descriptor.Version.Minor = Desc->Version.Minor;
102
0
    Descriptor.Version.Patch = Desc->Version.Patch;
103
0
    Descriptor.Version.Build = Desc->Version.Build;
104
0
    Descriptor.ModuleCount = Desc->ModuleCount;
105
0
    Descriptor.ModuleDescriptions = ModuleDescriptions.data();
106
0
    Descriptor.AddOptions = &addOptionsWrapper;
107
108
0
    for (size_t I = 0; I < Desc->ProgramOptionCount; ++I) {
109
0
      const auto *OptionDesc = &Desc->ProgramOptions[I];
110
0
      auto Emplace = [OptionDesc, this](auto InPlaceType, auto *Storage,
111
0
                                        auto *DefaultValue) {
112
0
        Options.emplace_back(
113
0
            std::piecewise_construct, std::tuple{OptionDesc},
114
0
            std::tuple{InPlaceType, PO::Description(OptionDesc->Description),
115
0
                       Storage, DefaultValue});
116
0
      };
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<std::__1::in_place_type_t::Toggle*, std::__1::in_place_type_t::Parser<bool> > >, bool, bool const>(std::__1::in_place_type_t<WasmEdge::PO::Option<std::__1::in_place_type_t::Toggle*, std::__1::in_place_type_t::Parser<bool> > >, bool*, bool const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<signed char*, std::__1::in_place_type_t::Parser<signed char> > >, signed char, signed char const>(std::__1::in_place_type_t<WasmEdge::PO::Option<signed char*, std::__1::in_place_type_t::Parser<signed char> > >, signed char*, signed char const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<short*, std::__1::in_place_type_t::Parser<short> > >, short, short const>(std::__1::in_place_type_t<WasmEdge::PO::Option<short*, std::__1::in_place_type_t::Parser<short> > >, short*, short const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<int*, std::__1::in_place_type_t::Parser<int> > >, int, int const>(std::__1::in_place_type_t<WasmEdge::PO::Option<int*, std::__1::in_place_type_t::Parser<int> > >, int*, int const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<long*, std::__1::in_place_type_t::Parser<long> > >, long, long const>(std::__1::in_place_type_t<WasmEdge::PO::Option<long*, std::__1::in_place_type_t::Parser<long> > >, long*, long const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<unsigned char*, std::__1::in_place_type_t::Parser<unsigned char> > >, unsigned char, unsigned char const>(std::__1::in_place_type_t<WasmEdge::PO::Option<unsigned char*, std::__1::in_place_type_t::Parser<unsigned char> > >, unsigned char*, unsigned char const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<unsigned short*, std::__1::in_place_type_t::Parser<unsigned short> > >, unsigned short, unsigned short const>(std::__1::in_place_type_t<WasmEdge::PO::Option<unsigned short*, std::__1::in_place_type_t::Parser<unsigned short> > >, unsigned short*, unsigned short const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<unsigned int*, std::__1::in_place_type_t::Parser<unsigned int> > >, unsigned int, unsigned int const>(std::__1::in_place_type_t<WasmEdge::PO::Option<unsigned int*, std::__1::in_place_type_t::Parser<unsigned int> > >, unsigned int*, unsigned int const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<unsigned long*, std::__1::in_place_type_t::Parser<unsigned long> > >, unsigned long, unsigned long const>(std::__1::in_place_type_t<WasmEdge::PO::Option<unsigned long*, std::__1::in_place_type_t::Parser<unsigned long> > >, unsigned long*, unsigned long const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<float*, std::__1::in_place_type_t::Parser<float> > >, float, float const>(std::__1::in_place_type_t<WasmEdge::PO::Option<float*, std::__1::in_place_type_t::Parser<float> > >, float*, float const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<double*, std::__1::in_place_type_t::Parser<double> > >, double, double const>(std::__1::in_place_type_t<WasmEdge::PO::Option<double*, std::__1::in_place_type_t::Parser<double> > >, double*, double const*) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::CAPIPluginRegister(WasmEdge_PluginDescriptor const*)::{lambda(auto:1, auto:2*, auto:3*)#1}::operator()<std::__1::in_place_type_t<WasmEdge::PO::Option<WasmEdge_String*, std::__1::in_place_type_t::Parser<WasmEdge::PO::Option> > >, WasmEdge::PO::Option, WasmEdge::PO::Option const>(std::__1::in_place_type_t<WasmEdge::PO::Option<WasmEdge_String*, std::__1::in_place_type_t::Parser<WasmEdge::PO::Option> > >, WasmEdge::PO::Option*, WasmEdge::PO::Option const*) const
117
0
      switch (Desc->ProgramOptions[I].Type) {
118
0
      case WasmEdge_ProgramOptionType_None:
119
0
        break;
120
0
      case WasmEdge_ProgramOptionType_Toggle:
121
0
        Emplace(std::in_place_type<PO::Option<PO::Toggle *>>,
122
0
                static_cast<bool *>(OptionDesc->Storage),
123
0
                static_cast<const bool *>(OptionDesc->DefaultValue));
124
0
        break;
125
0
      case WasmEdge_ProgramOptionType_Int8:
126
0
        Emplace(std::in_place_type<PO::Option<int8_t *>>,
127
0
                static_cast<int8_t *>(OptionDesc->Storage),
128
0
                static_cast<const int8_t *>(OptionDesc->DefaultValue));
129
0
        break;
130
0
      case WasmEdge_ProgramOptionType_Int16:
131
0
        Emplace(std::in_place_type<PO::Option<int16_t *>>,
132
0
                static_cast<int16_t *>(OptionDesc->Storage),
133
0
                static_cast<const int16_t *>(OptionDesc->DefaultValue));
134
0
        break;
135
0
      case WasmEdge_ProgramOptionType_Int32:
136
0
        Emplace(std::in_place_type<PO::Option<int32_t *>>,
137
0
                static_cast<int32_t *>(OptionDesc->Storage),
138
0
                static_cast<const int32_t *>(OptionDesc->DefaultValue));
139
0
        break;
140
0
      case WasmEdge_ProgramOptionType_Int64:
141
0
        Emplace(std::in_place_type<PO::Option<int64_t *>>,
142
0
                static_cast<int64_t *>(OptionDesc->Storage),
143
0
                static_cast<const int64_t *>(OptionDesc->DefaultValue));
144
0
        break;
145
0
      case WasmEdge_ProgramOptionType_UInt8:
146
0
        Emplace(std::in_place_type<PO::Option<uint8_t *>>,
147
0
                static_cast<uint8_t *>(OptionDesc->Storage),
148
0
                static_cast<const uint8_t *>(OptionDesc->DefaultValue));
149
0
        break;
150
0
      case WasmEdge_ProgramOptionType_UInt16:
151
0
        Emplace(std::in_place_type<PO::Option<uint16_t *>>,
152
0
                static_cast<uint16_t *>(OptionDesc->Storage),
153
0
                static_cast<const uint16_t *>(OptionDesc->DefaultValue));
154
0
        break;
155
0
      case WasmEdge_ProgramOptionType_UInt32:
156
0
        Emplace(std::in_place_type<PO::Option<uint32_t *>>,
157
0
                static_cast<uint32_t *>(OptionDesc->Storage),
158
0
                static_cast<const uint32_t *>(OptionDesc->DefaultValue));
159
0
        break;
160
0
      case WasmEdge_ProgramOptionType_UInt64:
161
0
        Emplace(std::in_place_type<PO::Option<uint64_t *>>,
162
0
                static_cast<uint64_t *>(OptionDesc->Storage),
163
0
                static_cast<const uint64_t *>(OptionDesc->DefaultValue));
164
0
        break;
165
0
      case WasmEdge_ProgramOptionType_Float:
166
0
        Emplace(std::in_place_type<PO::Option<float *>>,
167
0
                static_cast<float *>(OptionDesc->Storage),
168
0
                static_cast<const float *>(OptionDesc->DefaultValue));
169
0
        break;
170
0
      case WasmEdge_ProgramOptionType_Double:
171
0
        Emplace(std::in_place_type<PO::Option<double *>>,
172
0
                static_cast<double *>(OptionDesc->Storage),
173
0
                static_cast<const double *>(OptionDesc->DefaultValue));
174
0
        break;
175
0
      case WasmEdge_ProgramOptionType_String:
176
0
        Emplace(std::in_place_type<PO::Option<WasmEdge_String *>>,
177
0
                static_cast<WasmEdge_String *>(OptionDesc->Storage),
178
0
                static_cast<const WasmEdge_String *>(OptionDesc->DefaultValue));
179
0
        break;
180
0
      }
181
0
    }
182
183
0
    Result = Plugin::registerPlugin(&Descriptor);
184
0
  }
185
0
  bool result() const noexcept { return Result; }
186
187
private:
188
  static Runtime::Instance::ModuleInstance *
189
0
  createWrapper(const PluginModule::ModuleDescriptor *Descriptor) noexcept {
190
0
    static_assert(std::is_standard_layout_v<CAPIPluginRegister>);
191
0
    if (auto Iter = DescriptionLookup.find(Descriptor);
192
0
        unlikely(Iter == DescriptionLookup.end())) {
193
0
      return nullptr;
194
0
    } else {
195
0
      return reinterpret_cast<Runtime::Instance::ModuleInstance *>(
196
0
          Iter->second->Create(Iter->second));
197
0
    }
198
0
  }
199
  static void addOptionsWrapper(const Plugin::PluginDescriptor *Descriptor,
200
                                PO::ArgumentParser &Parser
201
0
                                [[maybe_unused]]) noexcept {
202
0
    const CAPIPluginRegister *This =
203
0
        reinterpret_cast<const CAPIPluginRegister *>(
204
0
            reinterpret_cast<uintptr_t>(Descriptor) -
205
0
            offsetof(CAPIPluginRegister, Descriptor));
206
0
    for (auto &Option : This->Options) {
207
0
      std::visit(
208
0
          [&Option, &Parser](auto &POOption) {
209
0
            Parser.add_option(Option.first->Name, POOption);
210
0
          },
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<WasmEdge::PO::Toggle*, WasmEdge::PO::Parser<bool> > >(WasmEdge::PO::Option<WasmEdge::PO::Toggle*, WasmEdge::PO::Parser<bool> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<signed char*, WasmEdge::PO::Parser<signed char> > >(WasmEdge::PO::Option<signed char*, WasmEdge::PO::Parser<signed char> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<short*, WasmEdge::PO::Parser<short> > >(WasmEdge::PO::Option<short*, WasmEdge::PO::Parser<short> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<int*, WasmEdge::PO::Parser<int> > >(WasmEdge::PO::Option<int*, WasmEdge::PO::Parser<int> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<long*, WasmEdge::PO::Parser<long> > >(WasmEdge::PO::Option<long*, WasmEdge::PO::Parser<long> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<unsigned char*, WasmEdge::PO::Parser<unsigned char> > >(WasmEdge::PO::Option<unsigned char*, WasmEdge::PO::Parser<unsigned char> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<unsigned short*, WasmEdge::PO::Parser<unsigned short> > >(WasmEdge::PO::Option<unsigned short*, WasmEdge::PO::Parser<unsigned short> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<unsigned int*, WasmEdge::PO::Parser<unsigned int> > >(WasmEdge::PO::Option<unsigned int*, WasmEdge::PO::Parser<unsigned int> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<unsigned long*, WasmEdge::PO::Parser<unsigned long> > >(WasmEdge::PO::Option<unsigned long*, WasmEdge::PO::Parser<unsigned long> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<float*, WasmEdge::PO::Parser<float> > >(WasmEdge::PO::Option<float*, WasmEdge::PO::Parser<float> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<double*, WasmEdge::PO::Parser<double> > >(WasmEdge::PO::Option<double*, WasmEdge::PO::Parser<double> >&) const
Unexecuted instantiation: plugin.cpp:auto WasmEdge::Plugin::(anonymous namespace)::CAPIPluginRegister::addOptionsWrapper(WasmEdge::Plugin::Plugin::PluginDescriptor const*, WasmEdge::PO::ArgumentParser&)::{lambda(auto:1&)#1}::operator()<WasmEdge::PO::Option<WasmEdge_String*, WasmEdge::PO::Parser<WasmEdge::PO::Option> > >(WasmEdge::PO::Option<WasmEdge_String*, WasmEdge::PO::Parser<WasmEdge::PO::Option> >&) const
211
0
          Option.second);
212
0
    }
213
0
  }
214
215
  Plugin::PluginDescriptor Descriptor;
216
  mutable std::vector<std::pair<
217
      const WasmEdge_ProgramOption *,
218
      std::variant<PO::Option<PO::Toggle *>, PO::Option<int8_t *>,
219
                   PO::Option<int16_t *>, PO::Option<int32_t *>,
220
                   PO::Option<int64_t *>, PO::Option<uint8_t *>,
221
                   PO::Option<uint16_t *>, PO::Option<uint32_t *>,
222
                   PO::Option<uint64_t *>, PO::Option<float *>,
223
                   PO::Option<double *>, PO::Option<WasmEdge_String *>>>>
224
      Options;
225
  std::vector<PluginModule::ModuleDescriptor> ModuleDescriptions;
226
  static std::unordered_map<const PluginModule::ModuleDescriptor *,
227
                            const WasmEdge_ModuleDescriptor *>
228
      DescriptionLookup;
229
230
  bool Result = false;
231
};
232
std::unordered_map<const PluginModule::ModuleDescriptor *,
233
                   const WasmEdge_ModuleDescriptor *>
234
    CAPIPluginRegister::DescriptionLookup;
235
236
std::vector<std::unique_ptr<CAPIPluginRegister>> CAPIPluginRegisters;
237
238
// Path keys of shared libraries already loaded, so repeated load() calls
239
// neither re-dlopen nor re-register them and still report them as available.
240
// Guarded by Plugin::Mutex.
241
std::unordered_set<std::string> LoadedFiles;
242
243
} // namespace
244
245
std::shared_mutex WasmEdge::Plugin::Plugin::Mutex;
246
std::deque<Plugin> WasmEdge::Plugin::Plugin::PluginRegistry;
247
std::unordered_map<std::string_view, std::size_t, Hash::Hash>
248
    WasmEdge::Plugin::Plugin::PluginNameLookup;
249
250
0
void Plugin::loadFromDefaultPaths() noexcept {
251
0
  registerBuiltInPlugins();
252
0
  for (const auto &Path : Plugin::Plugin::getDefaultPluginPaths()) {
253
0
    Plugin::Plugin::load(Path);
254
0
  }
255
0
}
256
257
0
std::vector<std::filesystem::path> Plugin::getDefaultPluginPaths() noexcept {
258
0
  using namespace std::literals::string_view_literals;
259
0
  std::vector<std::filesystem::path> Result;
260
0
  std::error_code Error;
261
262
  // Extra directories from environ variable
263
0
  if (const auto ExtraEnv = ::getenv("WASMEDGE_PLUGIN_PATH")) {
264
0
    std::string_view ExtraEnvStr = ExtraEnv;
265
0
    for (auto Sep = ExtraEnvStr.find(':'); Sep != std::string_view::npos;
266
0
         Sep = ExtraEnvStr.find(':')) {
267
0
      Result.push_back(std::filesystem::u8path(ExtraEnvStr.substr(0, Sep)));
268
0
      const auto Next = ExtraEnvStr.find_first_not_of(':', Sep);
269
0
      ExtraEnvStr = ExtraEnvStr.substr(Next);
270
0
    }
271
0
    Result.push_back(std::filesystem::u8path(ExtraEnvStr));
272
0
  }
273
274
  // Plugin directory for the WasmEdge installation.
275
0
#if WASMEDGE_OS_LINUX || WASMEDGE_OS_MACOS
276
0
  Dl_info DLInfo;
277
0
  int Status =
278
0
      dladdr(reinterpret_cast<void *>(Plugin::getDefaultPluginPaths), &DLInfo);
279
0
  if (Status != 0) {
280
0
    if (DLInfo.dli_fname == nullptr) {
281
0
      spdlog::error(
282
0
          "Address matched to a shared object but not to any symbol "sv
283
0
          "within the object. dli_fname is null."sv);
284
0
      return std::vector<std::filesystem::path>();
285
0
    }
286
0
    auto LibPath = std::filesystem::u8path(DLInfo.dli_fname)
287
0
                       .parent_path()
288
0
                       .lexically_normal();
289
0
    const auto UsrStr = "/usr"sv;
290
0
    const auto LibStr = "/lib"sv;
291
0
    const auto &PathStr = LibPath.native();
292
0
    if ((PathStr.size() >= UsrStr.size() &&
293
0
         std::equal(UsrStr.begin(), UsrStr.end(), PathStr.begin())) ||
294
0
        (PathStr.size() >= LibStr.size() &&
295
0
         std::equal(LibStr.begin(), LibStr.end(), PathStr.begin()))) {
296
      // The installation path of the WasmEdge library is under "/usr".
297
      // Plug-in path will be in "LIB_PATH/wasmedge".
298
      // If the installation path is under "/usr/lib" or "/usr/lib64", the
299
      // traced library path will be "/lib" or "/lib64".
300
0
      Result.push_back(LibPath / std::filesystem::u8path("wasmedge"sv));
301
0
    } else {
302
      // The installation path of the WasmEdge library is not under "/usr", such
303
      // as "$HOME/.wasmedge". Plug-in path will be in "LIB_PATH/../plugin".
304
0
      Result.push_back(LibPath / std::filesystem::u8path(".."sv) /
305
0
                       std::filesystem::u8path("plugin"sv));
306
0
    }
307
0
  } else {
308
0
    spdlog::error(ErrCode::Value::NonNullRequired);
309
0
    spdlog::error("Address could not be matched to any shared object. "sv
310
0
                  "Detailed error information is not available."sv);
311
0
    return std::vector<std::filesystem::path>();
312
0
  }
313
#elif WASMEDGE_OS_WINDOWS
314
  // Global plugin directory.
315
  if (std::filesystem::path Path; GetFunctionModuleFileName(
316
          reinterpret_cast<void *>(Plugin::getDefaultPluginPaths), Path)) {
317
    Result.push_back(Path.parent_path());
318
  } else {
319
    spdlog::error("Failed to get the path of the current module."sv);
320
    return std::vector<std::filesystem::path>();
321
  }
322
323
  // Local home plugin directory.
324
  std::filesystem::path Home;
325
  if (const auto HomeEnv = ::getenv("USERPROFILE")) {
326
    Home = std::filesystem::u8path(HomeEnv);
327
  } else {
328
#if NTDDI_VERSION >= NTDDI_VISTA
329
    wchar_t *Path = nullptr;
330
    if (winapi::HRESULT_ Res = winapi::SHGetKnownFolderPath(
331
            winapi::FOLDERID_Profile, 0, nullptr, &Path);
332
        winapi::SUCCEEDED_(Res)) {
333
      Home = std::filesystem::path(Path);
334
      winapi::CoTaskMemFree(Path);
335
    }
336
#else
337
    wchar_t Path[winapi::MAX_PATH_];
338
    if (winapi::HRESULT_ Res = winapi::SHGetFolderPathW(
339
            nullptr, winapi::CSIDL_PROFILE_, nullptr, 0, Path);
340
        winapi::SUCCEEDED_(Res)) {
341
      Home = std::filesystem::path(Path);
342
    }
343
#endif
344
  }
345
  Result.push_back(Home / std::filesystem::u8path(".wasmedge"sv) /
346
                   std::filesystem::u8path("plugin"sv));
347
#endif
348
349
0
  return Result;
350
0
}
351
352
0
WASMEDGE_EXPORT bool Plugin::load(const std::filesystem::path &Path) noexcept {
353
0
  std::error_code Error;
354
0
  auto Status = std::filesystem::status(Path, Error);
355
0
  if (likely(!Error)) {
356
0
    if (std::filesystem::is_directory(Status)) {
357
0
      bool Result = false;
358
0
      for (const auto &Entry : std::filesystem::recursive_directory_iterator(
359
0
               Path, std::filesystem::directory_options::skip_permission_denied,
360
0
               Error)) {
361
0
        const auto &EntryPath = Entry.path();
362
0
        if (Entry.is_regular_file(Error) &&
363
0
            EntryPath.extension().u8string() == WASMEDGE_LIB_EXTENSION) {
364
0
          Result |= loadFile(EntryPath);
365
0
        }
366
0
      }
367
0
      return Result;
368
0
    } else if (std::filesystem::is_regular_file(Status) &&
369
0
               Path.extension().u8string() == WASMEDGE_LIB_EXTENSION) {
370
0
      return loadFile(Path);
371
0
    }
372
0
  }
373
0
  return false;
374
0
}
375
376
0
bool Plugin::registerPlugin(const PluginDescriptor *Desc) noexcept {
377
0
  if (Desc->APIVersion != CurrentAPIVersion) {
378
0
    spdlog::debug(
379
0
        "Plugin: API version {} of plugin {} is not match to current {}."sv,
380
0
        Desc->APIVersion, Desc->Name, CurrentAPIVersion);
381
0
    return false;
382
0
  }
383
0
  if (PluginNameLookup.find(Desc->Name) != PluginNameLookup.end()) {
384
0
    spdlog::debug("Plugin: {} has already loaded."sv, Desc->Name);
385
0
    return false;
386
0
  }
387
388
0
  const auto Index = PluginRegistry.size();
389
0
  PluginRegistry.emplace_back(Desc);
390
0
  PluginNameLookup.emplace(Desc->Name, Index);
391
392
0
  return true;
393
0
}
394
395
0
void Plugin::addPluginOptions(PO::ArgumentParser &Parser) noexcept {
396
0
  std::shared_lock Lock(Mutex);
397
0
  for (const auto &Plugin : PluginRegistry) {
398
0
    if (Plugin.Desc->AddOptions) {
399
0
      Plugin.Desc->AddOptions(Plugin.Desc, Parser);
400
0
    }
401
0
  }
402
0
}
403
404
0
WASMEDGE_EXPORT const Plugin *Plugin::find(std::string_view Name) noexcept {
405
0
  std::shared_lock Lock(Mutex);
406
0
  if (auto Iter = PluginNameLookup.find(Name); Iter != PluginNameLookup.end()) {
407
    // Safe to return after releasing the lock: std::deque never relocates
408
    // existing elements when later plugins are appended.
409
0
    return std::addressof(PluginRegistry[Iter->second]);
410
0
  }
411
0
  return nullptr;
412
0
}
413
414
0
const std::deque<Plugin> &Plugin::plugins() noexcept { return PluginRegistry; }
415
416
0
bool Plugin::loadFile(const std::filesystem::path &Path) noexcept {
417
0
  std::unique_lock Lock(Mutex);
418
419
  // A library already loaded in this process stays available: report success
420
  // without re-dlopen'ing or re-registering it.
421
0
  const std::string FileKey = Path.u8string();
422
0
  if (LoadedFiles.find(FileKey) != LoadedFiles.end()) {
423
0
    return true;
424
0
  }
425
426
0
  bool Result = false;
427
0
  auto Lib = std::make_shared<Loader::SharedLibrary>();
428
0
  if (auto Res = Lib->load(Path); unlikely(!Res)) {
429
0
    return false;
430
0
  }
431
432
0
  if (auto GetDescriptor =
433
0
          Lib->get<Plugin::PluginDescriptor const *()>("GetDescriptor")) {
434
0
    Result = Plugin::registerPlugin(GetDescriptor());
435
0
  }
436
437
0
  if (!Result) {
438
    // Check C interface
439
0
    if (auto GetDescriptor = Lib->get<decltype(WasmEdge_Plugin_GetDescriptor)>(
440
0
            "WasmEdge_Plugin_GetDescriptor");
441
0
        unlikely(!GetDescriptor)) {
442
0
      return false;
443
0
    } else if (const auto *Descriptor = GetDescriptor();
444
0
               unlikely(!Descriptor)) {
445
0
      return false;
446
0
    } else if (PluginNameLookup.find(Descriptor->Name) !=
447
0
               PluginNameLookup.end()) {
448
      // Already registered under this name: don't build a CAPIPluginRegister,
449
      // it would leak since registerPlugin() would reject the duplicate.
450
0
      return false;
451
0
    } else {
452
0
      Result =
453
0
          CAPIPluginRegisters
454
0
              .emplace_back(std::make_unique<CAPIPluginRegister>(Descriptor))
455
0
              ->result();
456
0
      if (unlikely(!Result)) {
457
0
        CAPIPluginRegisters.pop_back();
458
0
      }
459
0
    }
460
0
  }
461
462
0
  if (!Result) {
463
0
    return false;
464
0
  }
465
466
0
  auto &Plugin = PluginRegistry.back();
467
0
  Plugin.Path = Path;
468
0
  Plugin.Lib = std::move(Lib);
469
0
  LoadedFiles.insert(FileKey);
470
0
  return true;
471
0
}
472
473
0
void Plugin::registerBuiltInPlugins() noexcept {
474
0
  std::unique_lock Lock(Mutex);
475
  // BUILTIN-PLUGIN: Register wasi-logging here. May be refactored in 0.15.0.
476
0
  registerPlugin(&Host::WasiLoggingModule::PluginDescriptor);
477
0
}
478
479
0
Plugin::Plugin(const PluginDescriptor *D) noexcept : Desc(D) {
480
0
  for (const auto &ModuleDesc : Span<const PluginModule::ModuleDescriptor>(
481
0
           D->ModuleDescriptions, D->ModuleCount)) {
482
0
    const auto Index = ModuleRegistry.size();
483
0
    ModuleRegistry.push_back(PluginModule(&ModuleDesc));
484
0
    ModuleNameLookup.emplace(ModuleDesc.Name, Index);
485
0
  }
486
0
  for (const auto &ComponentDesc :
487
0
       Span<const PluginComponent::ComponentDescriptor>(
488
0
           D->ComponentDescriptions, D->ComponentCount)) {
489
0
    const auto Index = ComponentRegistry.size();
490
0
    ComponentRegistry.push_back(PluginComponent(&ComponentDesc));
491
0
    ComponentNameLookup.emplace(ComponentDesc.Name, Index);
492
0
  }
493
0
}
494
495
WASMEDGE_EXPORT const PluginModule *
496
0
Plugin::findModule(std::string_view Name) const noexcept {
497
0
  if (auto Iter = ModuleNameLookup.find(Name); Iter != ModuleNameLookup.end()) {
498
0
    return std::addressof(ModuleRegistry[Iter->second]);
499
0
  }
500
0
  return nullptr;
501
0
}
502
503
WASMEDGE_EXPORT const PluginComponent *
504
0
Plugin::findComponent(std::string_view Name) const noexcept {
505
0
  if (auto Iter = ComponentNameLookup.find(Name);
506
0
      Iter != ComponentNameLookup.end()) {
507
0
    return std::addressof(ComponentRegistry[Iter->second]);
508
0
  }
509
0
  return nullptr;
510
0
}
511
} // namespace Plugin
512
} // namespace WasmEdge