Coverage Report

Created: 2026-09-28 06:22

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