Coverage Report

Created: 2026-06-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/api/wasmedge_compat.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
// >>>>>>>> WasmEdge compat ABI shims >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
5
//
6
// Several C API functions changed their signatures across releases. Each such
7
// change pins the previous ABI under its historical version node (bound with
8
// `.symver`) while `wasmedge.cpp` keeps the new default version, so binaries
9
// built against the old library keep resolving the old symbol.
10
//
11
//   - 0.16 -> 0.17: five functions changed from the by-value `WasmEdge_Limit`
12
//     struct to the opaque `WasmEdge_LimitContext` handle. Old ABI pinned at
13
//     `@WASMEDGE_0.16`, new default `@@WASMEDGE_0.17`.
14
//   - 0.17 -> 0.18: the four `WasmEdge_ModuleInstanceAdd{Function,Table,Memory,
15
//     Global}` functions changed their return type from `void` to
16
//     `WasmEdge_Result`. Old ABI pinned at `@WASMEDGE_0.17`, new default
17
//     `@@WASMEDGE_0.18`.
18
//
19
// The shims are frozen copies of the previous-release implementations so the
20
// old ABIs stay pinned bit-for-bit.
21
//
22
// Compiled only into the shared library on Linux/Android (see CMakeLists.txt).
23
// Kept in a separate TU because under ThinLTO the module-level `.symver` asm
24
// would otherwise collide with the in-IR definition of the same public symbol.
25
26
#include "wasmedge/wasmedge.h"
27
28
#include "ast/type.h"
29
#include "runtime/instance/module.h"
30
31
#include <algorithm>
32
#include <array>
33
#include <cstdint>
34
#include <memory>
35
#include <string_view>
36
37
#if defined(__ELF__) && (defined(__linux__) || defined(__ANDROID__))
38
39
namespace {
40
using namespace WasmEdge;
41
42
// Local copies of the `wasmedge.cpp` helpers, kept here to stay self-contained.
43
0
inline ValType genValType(const WasmEdge_ValType &T) noexcept {
44
0
  std::array<uint8_t, 8> R;
45
0
  std::copy_n(T.Data, 8, R.begin());
46
0
  return ValType(R);
47
0
}
48
0
inline auto *toTabTypeCxt(AST::TableType *Cxt) noexcept {
49
0
  return reinterpret_cast<WasmEdge_TableTypeContext *>(Cxt);
50
0
}
51
inline const auto *
52
0
fromTabTypeCxt(const WasmEdge_TableTypeContext *Cxt) noexcept {
53
0
  return reinterpret_cast<const AST::TableType *>(Cxt);
54
0
}
55
0
inline auto *toMemTypeCxt(AST::MemoryType *Cxt) noexcept {
56
0
  return reinterpret_cast<WasmEdge_MemoryTypeContext *>(Cxt);
57
0
}
58
inline const auto *
59
0
fromMemTypeCxt(const WasmEdge_MemoryTypeContext *Cxt) noexcept {
60
0
  return reinterpret_cast<const AST::MemoryType *>(Cxt);
61
0
}
62
0
inline std::string_view genStrView(const WasmEdge_String S) noexcept {
63
0
  return std::string_view(S.Buf, S.Length);
64
0
}
65
0
inline auto *fromModCxt(WasmEdge_ModuleInstanceContext *Cxt) noexcept {
66
0
  return reinterpret_cast<Runtime::Instance::ModuleInstance *>(Cxt);
67
0
}
68
0
inline auto *fromFuncCxt(WasmEdge_FunctionInstanceContext *Cxt) noexcept {
69
0
  return reinterpret_cast<Runtime::Instance::FunctionInstance *>(Cxt);
70
0
}
71
0
inline auto *fromTabCxt(WasmEdge_TableInstanceContext *Cxt) noexcept {
72
0
  return reinterpret_cast<Runtime::Instance::TableInstance *>(Cxt);
73
0
}
74
0
inline auto *fromMemCxt(WasmEdge_MemoryInstanceContext *Cxt) noexcept {
75
0
  return reinterpret_cast<Runtime::Instance::MemoryInstance *>(Cxt);
76
0
}
77
0
inline auto *fromGlobCxt(WasmEdge_GlobalInstanceContext *Cxt) noexcept {
78
0
  return reinterpret_cast<Runtime::Instance::GlobalInstance *>(Cxt);
79
0
}
80
} // namespace
81
82
extern "C" {
83
84
// Layout-compatible revival of the 0.16 `WasmEdge_Limit` struct. `Min`/`Max`
85
// are `uint32_t` (Memory64 widened them to 64-bit only in 0.17), keeping the
86
// by-value ABI bit-for-bit identical to what 0.16 consumers were built against.
87
typedef struct WasmEdge_Limit_0_16 {
88
  bool HasMax;
89
  bool Shared;
90
  uint32_t Min;
91
  uint32_t Max;
92
} WasmEdge_Limit_0_16;
93
94
__attribute__((used, visibility("default"))) bool
95
WasmEdge_LimitIsEqual_Compat_016(const WasmEdge_Limit_0_16 Lim1,
96
0
                                 const WasmEdge_Limit_0_16 Lim2) {
97
0
  return Lim1.HasMax == Lim2.HasMax && Lim1.Shared == Lim2.Shared &&
98
0
         Lim1.Min == Lim2.Min && Lim1.Max == Lim2.Max;
99
0
}
100
101
__attribute__((used, visibility("default"))) WasmEdge_TableTypeContext *
102
WasmEdge_TableTypeCreate_Compat_016(const WasmEdge_ValType RefType,
103
0
                                    const WasmEdge_Limit_0_16 Limit) {
104
0
  WasmEdge::ValType RT = genValType(RefType);
105
0
  if (!RT.isRefType()) {
106
0
    return nullptr;
107
0
  }
108
0
  if (Limit.HasMax) {
109
0
    return toTabTypeCxt(new WasmEdge::AST::TableType(RT, Limit.Min, Limit.Max));
110
0
  } else {
111
0
    return toTabTypeCxt(new WasmEdge::AST::TableType(RT, Limit.Min));
112
0
  }
113
0
}
114
115
__attribute__((used, visibility("default"))) WasmEdge_Limit_0_16
116
0
WasmEdge_TableTypeGetLimit_Compat_016(const WasmEdge_TableTypeContext *Cxt) {
117
0
  if (Cxt) {
118
0
    const auto &Lim = fromTabTypeCxt(Cxt)->getLimit();
119
0
    return WasmEdge_Limit_0_16{/* HasMax */ Lim.hasMax(),
120
0
                               /* Shared */ Lim.isShared(),
121
0
                               /* Min */ static_cast<uint32_t>(Lim.getMin()),
122
0
                               /* Max */ static_cast<uint32_t>(Lim.getMax())};
123
0
  }
124
0
  return WasmEdge_Limit_0_16{/* HasMax */ false, /* Shared */ false,
125
0
                             /* Min */ 0, /* Max */ 0};
126
0
}
127
128
__attribute__((used, visibility("default"))) WasmEdge_MemoryTypeContext *
129
0
WasmEdge_MemoryTypeCreate_Compat_016(const WasmEdge_Limit_0_16 Limit) {
130
0
  if (Limit.Shared) {
131
0
    return toMemTypeCxt(
132
0
        new WasmEdge::AST::MemoryType(Limit.Min, Limit.Max, true));
133
0
  } else if (Limit.HasMax) {
134
0
    return toMemTypeCxt(new WasmEdge::AST::MemoryType(Limit.Min, Limit.Max));
135
0
  } else {
136
0
    return toMemTypeCxt(new WasmEdge::AST::MemoryType(Limit.Min));
137
0
  }
138
0
}
139
140
__attribute__((used, visibility("default"))) WasmEdge_Limit_0_16
141
0
WasmEdge_MemoryTypeGetLimit_Compat_016(const WasmEdge_MemoryTypeContext *Cxt) {
142
0
  if (Cxt) {
143
0
    const auto &Lim = fromMemTypeCxt(Cxt)->getLimit();
144
0
    return WasmEdge_Limit_0_16{/* HasMax */ Lim.hasMax(),
145
0
                               /* Shared */ Lim.isShared(),
146
0
                               /* Min */ static_cast<uint32_t>(Lim.getMin()),
147
0
                               /* Max */ static_cast<uint32_t>(Lim.getMax())};
148
0
  }
149
0
  return WasmEdge_Limit_0_16{/* HasMax */ false, /* Shared */ false,
150
0
                             /* Min */ 0, /* Max */ 0};
151
0
}
152
153
// The 0.17 -> 0.18 transition changed these four functions from returning
154
// `void` to returning `WasmEdge_Result`. These shims pin the old `void` ABI:
155
// they perform the same best-effort add and discard the result, matching the
156
// 0.17 behavior.
157
__attribute__((used, visibility("default"))) void
158
WasmEdge_ModuleInstanceAddFunction_Compat_017(
159
    WasmEdge_ModuleInstanceContext *Cxt, const WasmEdge_String Name,
160
0
    WasmEdge_FunctionInstanceContext *FuncCxt) {
161
0
  if (Cxt && FuncCxt) {
162
0
    static_cast<void>(fromModCxt(Cxt)->addHostFunc(
163
0
        genStrView(Name), std::unique_ptr<Runtime::Instance::FunctionInstance>(
164
0
                              fromFuncCxt(FuncCxt))));
165
0
  }
166
0
}
167
168
__attribute__((used, visibility("default"))) void
169
WasmEdge_ModuleInstanceAddTable_Compat_017(
170
    WasmEdge_ModuleInstanceContext *Cxt, const WasmEdge_String Name,
171
0
    WasmEdge_TableInstanceContext *TableCxt) {
172
0
  if (Cxt && TableCxt) {
173
0
    static_cast<void>(fromModCxt(Cxt)->addHostTable(
174
0
        genStrView(Name), std::unique_ptr<Runtime::Instance::TableInstance>(
175
0
                              fromTabCxt(TableCxt))));
176
0
  }
177
0
}
178
179
__attribute__((used, visibility("default"))) void
180
WasmEdge_ModuleInstanceAddMemory_Compat_017(
181
    WasmEdge_ModuleInstanceContext *Cxt, const WasmEdge_String Name,
182
0
    WasmEdge_MemoryInstanceContext *MemoryCxt) {
183
0
  if (Cxt && MemoryCxt) {
184
0
    static_cast<void>(fromModCxt(Cxt)->addHostMemory(
185
0
        genStrView(Name), std::unique_ptr<Runtime::Instance::MemoryInstance>(
186
0
                              fromMemCxt(MemoryCxt))));
187
0
  }
188
0
}
189
190
__attribute__((used, visibility("default"))) void
191
WasmEdge_ModuleInstanceAddGlobal_Compat_017(
192
    WasmEdge_ModuleInstanceContext *Cxt, const WasmEdge_String Name,
193
0
    WasmEdge_GlobalInstanceContext *GlobalCxt) {
194
0
  if (Cxt && GlobalCxt) {
195
0
    static_cast<void>(fromModCxt(Cxt)->addHostGlobal(
196
0
        genStrView(Name), std::unique_ptr<Runtime::Instance::GlobalInstance>(
197
0
                              fromGlobCxt(GlobalCxt))));
198
0
  }
199
0
}
200
201
} // extern "C"
202
203
// Bind each shim to its historical `@WASMEDGE_0.16` symbol name. The shims need
204
// default visibility for `.symver` to export the alias; their own
205
// `WasmEdge_*_Compat_016` names are hidden via the `local:` list in
206
// `lib/api/libwasmedge.lds` so only the `@WASMEDGE_0.16` aliases are exported.
207
__asm__(".symver WasmEdge_LimitIsEqual_Compat_016, "
208
        "WasmEdge_LimitIsEqual@WASMEDGE_0.16");
209
__asm__(".symver WasmEdge_TableTypeCreate_Compat_016, "
210
        "WasmEdge_TableTypeCreate@WASMEDGE_0.16");
211
__asm__(".symver WasmEdge_TableTypeGetLimit_Compat_016, "
212
        "WasmEdge_TableTypeGetLimit@WASMEDGE_0.16");
213
__asm__(".symver WasmEdge_MemoryTypeCreate_Compat_016, "
214
        "WasmEdge_MemoryTypeCreate@WASMEDGE_0.16");
215
__asm__(".symver WasmEdge_MemoryTypeGetLimit_Compat_016, "
216
        "WasmEdge_MemoryTypeGetLimit@WASMEDGE_0.16");
217
__asm__(".symver WasmEdge_ModuleInstanceAddFunction_Compat_017, "
218
        "WasmEdge_ModuleInstanceAddFunction@WASMEDGE_0.17");
219
__asm__(".symver WasmEdge_ModuleInstanceAddTable_Compat_017, "
220
        "WasmEdge_ModuleInstanceAddTable@WASMEDGE_0.17");
221
__asm__(".symver WasmEdge_ModuleInstanceAddMemory_Compat_017, "
222
        "WasmEdge_ModuleInstanceAddMemory@WASMEDGE_0.17");
223
__asm__(".symver WasmEdge_ModuleInstanceAddGlobal_Compat_017, "
224
        "WasmEdge_ModuleInstanceAddGlobal@WASMEDGE_0.17");
225
226
#endif // __ELF__ && (__linux__ || __ANDROID__)
227
228
// <<<<<<<< WasmEdge compat ABI shims <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<