Coverage Report

Created: 2026-06-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/include/runtime/instance/memory.h
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
//===-- wasmedge/runtime/instance/memory.h - Memory Instance definition ---===//
5
//
6
// Part of the WasmEdge Project.
7
//
8
//===----------------------------------------------------------------------===//
9
///
10
/// \file
11
/// This file contains the memory instance definition in store manager.
12
///
13
//===----------------------------------------------------------------------===//
14
#pragma once
15
16
#include "ast/type.h"
17
#include "common/errcode.h"
18
#include "common/errinfo.h"
19
#include "common/int128.h"
20
#include "common/spdlog.h"
21
#include "common/types.h"
22
#include "system/allocator.h"
23
24
#include <algorithm>
25
#include <condition_variable>
26
#include <cstdint>
27
#include <cstring>
28
#include <fstream>
29
#include <memory>
30
#include <mutex>
31
#include <set>
32
#include <type_traits>
33
#include <unordered_map>
34
#include <utility>
35
36
namespace WasmEdge {
37
namespace Runtime {
38
namespace Instance {
39
40
class MemoryInstance {
41
42
public:
43
  static inline constexpr const uint64_t kPageSize = UINT64_C(65536);
44
  static inline constexpr const uint64_t kPageLimit32 = UINT64_C(0x10000);
45
  static inline constexpr const uint64_t kPageLimit64 =
46
      UINT64_C(0x1000000000000);
47
  MemoryInstance() = delete;
48
  MemoryInstance(MemoryInstance &&Inst) noexcept
49
      : MemType(Inst.MemType), DataPtr(Inst.DataPtr),
50
0
        PageLimit(Inst.PageLimit) {
51
0
    Inst.DataPtr = nullptr;
52
0
  }
53
  MemoryInstance(const AST::MemoryType &MType,
54
                 uint64_t PageLim = kPageLimit64) noexcept
55
0
      : MemType(MType), PageLimit(PageLim) {
56
0
    using namespace std::literals;
57
0
    if (MemType.getLimit().is32() && PageLimit > kPageLimit32) {
58
0
      if (PageLimit != kPageLimit64) {
59
        // Only log error when the page limit is not the default value of 64-bit
60
        // memory.
61
0
        spdlog::error("Memory Instance: Limited {} page larger than the 32-bit "
62
0
                      "page upper bound {}."sv,
63
0
                      PageLimit, kPageLimit32);
64
0
      }
65
0
      PageLimit = kPageLimit32;
66
0
    }
67
0
    if (MemType.getLimit().getMin() > PageLimit) {
68
0
      spdlog::error("Memory Instance: Limited {} page in configuration."sv,
69
0
                    PageLimit);
70
0
      MemType.getLimit().setMin(PageLimit);
71
0
    }
72
0
    DataPtr = Allocator::allocate(MemType.getLimit().getMin());
73
0
    if (DataPtr == nullptr) {
74
0
      spdlog::error("Memory Instance: Unable to find usable memory address."sv);
75
0
      MemType.getLimit().setMin(0U);
76
0
      return;
77
0
    }
78
0
  }
79
0
  ~MemoryInstance() noexcept {
80
0
    Allocator::release(DataPtr, MemType.getLimit().getMin());
81
0
  }
82
83
0
  bool isShared() const noexcept { return MemType.getLimit().isShared(); }
84
85
  /// Get page size of memory.data
86
0
  uint64_t getPageSize() const noexcept {
87
    // The memory page size is bound to the limit in the memory type.
88
0
    return MemType.getLimit().getMin();
89
0
  }
90
91
  /// Get memory size of memory.data
92
0
  uint64_t getSize() const noexcept {
93
    // The memory page size is bound to the limit in the memory type.
94
0
    return MemType.getLimit().getMin() * kPageSize;
95
0
  }
96
97
  /// Getter for memory type.
98
0
  const AST::MemoryType &getMemoryType() const noexcept { return MemType; }
99
100
  /// Check access size is valid.
101
  bool checkAccessBound(const uint64_t Offset,
102
0
                        const uint64_t Length) const noexcept {
103
    // Due to applying the Memory64 proposal, we should avoid the overflow issue
104
    // of the following code:
105
    //   return Offset + Length <= Limit;
106
0
    const uint64_t Limit = MemType.getLimit().getMin() * kPageSize;
107
0
    return std::numeric_limits<uint64_t>::max() - Offset >= Length &&
108
0
           Offset + Length <= Limit;
109
0
  }
110
111
  /// Grow page
112
0
  bool growPage(const uint64_t Count) noexcept {
113
0
    if (Count == 0) {
114
0
      return true;
115
0
    }
116
0
    uint64_t MaxPageCaped =
117
0
        MemType.getLimit().is32() ? kPageLimit32 : kPageLimit64;
118
0
    const uint64_t Min = MemType.getLimit().getMin();
119
0
    assuming(MaxPageCaped >= Min);
120
0
    if (MemType.getLimit().hasMax()) {
121
0
      const uint64_t Max = MemType.getLimit().getMax();
122
0
      MaxPageCaped = std::min(Max, MaxPageCaped);
123
0
    }
124
0
    if (Count > MaxPageCaped - Min) {
125
0
      return false;
126
0
    }
127
0
    assuming(PageLimit >= Min);
128
0
    if (Count > PageLimit - Min) {
129
0
      spdlog::error("Memory Instance: Memory grow page failed, exceeded "
130
0
                    "limited {} page size in configuration.",
131
0
                    PageLimit);
132
0
      return false;
133
0
    }
134
0
    if (auto NewPtr = Allocator::resize(DataPtr, Min, Min + Count);
135
0
        NewPtr == nullptr) {
136
0
      return false;
137
0
    } else {
138
0
      DataPtr = NewPtr;
139
0
    }
140
0
    MemType.getLimit().setMin(Min + Count);
141
0
    return true;
142
0
  }
143
144
  /// Get slice of Data[Offset : Offset + Length - 1]
145
  Expect<Span<Byte>> getBytes(const uint64_t Offset,
146
0
                              const uint64_t Length) const noexcept {
147
    // Check the memory boundary.
148
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
149
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
150
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
151
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
152
0
    }
153
0
    return Span<Byte>(&DataPtr[Offset], Length);
154
0
  }
155
156
  /// Replace the bytes of Data[Offset :] by Slice[Start : Start + Length - 1]
157
  Expect<void> setBytes(Span<const Byte> Slice, const uint64_t Offset,
158
0
                        const uint64_t Start, const uint64_t Length) noexcept {
159
    // Check the memory boundary.
160
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
161
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
162
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
163
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
164
0
    }
165
166
    // Check the input data validation.
167
0
    if (unlikely(std::numeric_limits<uint64_t>::max() - Start < Length ||
168
0
                 Start + Length > static_cast<uint64_t>(Slice.size()))) {
169
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
170
0
      spdlog::error(ErrInfo::InfoBoundary(Start, Length,
171
0
                                          static_cast<uint64_t>(Slice.size())));
172
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
173
0
    }
174
175
    // Copy the data.
176
0
    if (likely(Length > 0)) {
177
0
      std::copy(Slice.begin() + Start, Slice.begin() + Start + Length,
178
0
                DataPtr + Offset);
179
0
    }
180
0
    return {};
181
0
  }
182
183
  /// Fill the bytes of Data[Offset : Offset + Length - 1] by Val.
184
  Expect<void> fillBytes(const uint8_t Val, const uint64_t Offset,
185
0
                         const uint64_t Length) noexcept {
186
    // Check the memory boundary.
187
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
188
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
189
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
190
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
191
0
    }
192
193
    // Copy the data.
194
0
    if (likely(Length > 0)) {
195
0
      std::fill(DataPtr + Offset, DataPtr + Offset + Length, Val);
196
0
    }
197
0
    return {};
198
0
  }
199
200
  /// Get an uint8 array from Data[Offset : Offset + Length - 1]
201
  Expect<void> getArray(uint8_t *Arr, const uint64_t Offset,
202
                        const uint64_t Length,
203
0
                        const bool IsReverse = false) const noexcept {
204
0
    // Check the memory boundary.
205
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
206
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
207
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
208
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
209
0
    }
210
0
    if (likely(Length > 0)) {
211
0
      // Copy the data.
212
0
      if (IsReverse) {
213
0
        std::reverse_copy(DataPtr + Offset, DataPtr + Offset + Length, Arr);
214
0
      } else {
215
0
        std::copy(DataPtr + Offset, DataPtr + Offset + Length, Arr);
216
0
      }
217
0
    }
218
0
    return {};
219
0
  }
220
221
  /// Replace Data[Offset : Offset + Length - 1] to an uint8 array
222
  Expect<void> setArray(const uint8_t *Arr, const uint64_t Offset,
223
                        const uint64_t Length,
224
0
                        const bool IsReverse = false) noexcept {
225
0
    // Check the memory boundary.
226
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
227
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
228
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
229
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
230
0
    }
231
0
    if (likely(Length > 0)) {
232
0
      // Copy the data.
233
0
      if (IsReverse) {
234
0
        std::reverse_copy(Arr, Arr + Length, DataPtr + Offset);
235
0
      } else {
236
0
        std::copy(Arr, Arr + Length, DataPtr + Offset);
237
0
      }
238
0
    }
239
0
    return {};
240
0
  }
241
242
  /// Get pointer to specific offset of memory or null.
243
  template <typename T>
244
  typename std::enable_if_t<std::is_pointer_v<T>, T>
245
  getPointerOrNull(const uint64_t Offset) const noexcept {
246
    using Type = std::remove_pointer_t<T>;
247
    if (Offset == 0 || unlikely(!checkAccessBound(Offset, sizeof(Type)))) {
248
      return nullptr;
249
    }
250
    return reinterpret_cast<T>(&DataPtr[Offset]);
251
  }
252
253
  /// Get pointer to specific offset of memory.
254
  template <typename T>
255
  typename std::enable_if_t<std::is_pointer_v<T>, T>
256
0
  getPointer(const uint64_t Offset) const noexcept {
257
0
    using Type = std::remove_pointer_t<T>;
258
0
    if (unlikely(!checkAccessBound(Offset, sizeof(Type)))) {
259
0
      return nullptr;
260
0
    }
261
0
    return reinterpret_cast<T>(&DataPtr[Offset]);
262
0
  }
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicImEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicIjEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicIiEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicIlEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicIhEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicItEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP17__wasi_addrinfo_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP17__wasi_sockaddr_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPjEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES7_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPmEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES7_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP15__wasi_fdstat_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP17__wasi_filestat_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP16__wasi_prestat_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPiEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES7_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP16__wasi_address_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP16__wasi_roflags_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPK17__wasi_addrinfo_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPKjEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPtEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES7_E4typeEm
263
264
  /// Get array of object with count at specific offset of memory.
265
  template <typename T>
266
0
  Span<T> getSpan(const uint64_t Offset, const uint64_t Count) const noexcept {
267
0
    uint64_t Size;
268
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
269
    uint128_t Num =
270
        static_cast<uint128_t>(sizeof(T)) * static_cast<uint128_t>(Count);
271
    if ((Num >> 64).high() != 0) {
272
      return Span<T>();
273
    }
274
    Size = Num.low();
275
#else
276
0
    if (unlikely(__builtin_mul_overflow(static_cast<uint64_t>(sizeof(T)), Count,
277
0
                                        &Size))) {
278
0
      return Span<T>();
279
0
    }
280
0
#endif
281
0
    if (unlikely(!checkAccessBound(Offset, Size))) {
282
0
      return Span<T>();
283
0
    }
284
0
    return Span<T>(reinterpret_cast<T *>(&DataPtr[Offset]), Count);
285
0
  }
Unexecuted instantiation: cxx20::span<unsigned char, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<unsigned char>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<unsigned char const, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<unsigned char const>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<unsigned int, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<unsigned int>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<__wasi_iovec_t, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<__wasi_iovec_t>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<__wasi_ciovec_t, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<__wasi_ciovec_t>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<char, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<char>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<__wasi_subscription_t const, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<__wasi_subscription_t const>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<__wasi_event_t, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<__wasi_event_t>(unsigned long, unsigned long) const
286
287
  /// Get array of object at specific offset of memory.
288
  std::string_view getStringView(const uint64_t Offset,
289
0
                                 const uint64_t Size) const noexcept {
290
0
    if (unlikely(!checkAccessBound(Offset, Size))) {
291
0
      return {};
292
0
    }
293
0
    return {reinterpret_cast<const char *>(&DataPtr[Offset]), Size};
294
0
  }
295
296
  /// Template for loading bytes and converting them to a value.
297
  ///
298
  /// Load bytes of the specified length and construct a value.
299
  /// Only output values of int32, uint32, int64, uint64, float, and double are
300
  /// allowed.
301
  ///
302
  /// \param Value the constructed output value.
303
  /// \param Offset the start offset in data array.
304
  ///
305
  /// \returns void on success, ErrCode on failure.
306
  template <typename T, uint32_t Length = sizeof(T)>
307
  typename std::enable_if_t<IsWasmNumV<T>, Expect<void>>
308
0
  loadValue(T &Value, const uint64_t Offset) const noexcept {
309
    // Check the data boundary.
310
0
    static_assert(Length <= sizeof(T));
311
    // Check the memory boundary.
312
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
313
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
314
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
315
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
316
0
    }
317
    // Load the data to the value.
318
0
    if (likely(Length > 0)) {
319
0
      if constexpr (std::is_floating_point_v<T>) {
320
        // Floating case. Do the memory copy.
321
0
        EndianValue<T> LoadValue;
322
0
        std::memcpy(&LoadValue.raw(), &DataPtr[Offset], Length);
323
0
        Value = LoadValue.le();
324
0
      } else {
325
0
        if constexpr (sizeof(T) > 8) {
326
0
          assuming(sizeof(T) == 16);
327
0
          EndianValue<T> LoadValue = 0U;
328
0
          std::memcpy(&LoadValue.raw(), &DataPtr[Offset], Length);
329
0
          Value = LoadValue.le();
330
0
        } else {
331
          // Integer case. Extend to the result type.
332
0
          EndianValue<uint64_t> LoadVal = 0;
333
0
          std::memcpy(&LoadVal.raw(), &DataPtr[Offset], Length);
334
0
          uint64_t Val = LoadVal.le();
335
0
          if (std::is_signed_v<T> && (Val >> (Length * 8 - 1))) {
336
            // Signed extension.
337
0
            for (unsigned int I = Length; I < 8; I++) {
338
0
              Val |= 0xFFULL << (I * 8);
339
0
            }
340
0
          }
341
0
          Value = static_cast<T>(Val);
342
0
        }
343
0
      }
344
0
    }
345
0
    return {};
346
0
  }
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned int, 4u>(unsigned int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned long, 8u>(unsigned long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<float>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<float, 4u>(float&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<double>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<double, 8u>(double&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<int, 1u>(int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned int, 1u>(unsigned int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<int, 2u>(int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned int, 2u>(unsigned int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<long, 1u>(long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned long, 1u>(unsigned long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<long, 2u>(long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned long, 2u>(unsigned long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<long, 4u>(long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned long, 4u>(unsigned long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned __int128>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned __int128, 16u>(unsigned __int128&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned __int128>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned __int128, 4u>(unsigned __int128&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned __int128>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned __int128, 8u>(unsigned __int128&, unsigned long) const
347
348
  /// Template for storing a value as bytes.
349
  ///
350
  /// Store the value using the specified byte length.
351
  /// Only input values of uint32, uint64, float, and double are allowed.
352
  ///
353
  /// \param Value the value to store into the data array.
354
  /// \param Offset the start offset in data array.
355
  ///
356
  /// \returns void on success, ErrCode on failure.
357
  template <typename T, uint32_t Length = sizeof(T)>
358
  typename std::enable_if_t<IsWasmNativeNumV<T>, Expect<void>>
359
0
  storeValue(const T &Value, const uint64_t Offset) noexcept {
360
    // Check the data boundary.
361
0
    static_assert(Length <= sizeof(T));
362
    // Check the memory boundary.
363
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
364
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
365
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
366
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
367
0
    }
368
    // Copy the value to memory.
369
0
    if (likely(Length > 0)) {
370
0
      T StoreValue = EndianValue<T>(Value).le();
371
0
      std::memcpy(&DataPtr[Offset], &StoreValue, Length);
372
0
    }
373
0
    return {};
374
0
  }
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int, 4u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned long, 8u>(unsigned long const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<float>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<float, 4u>(float const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<double>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<double, 8u>(double const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int, 1u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int, 2u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned long, 1u>(unsigned long const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned long, 2u>(unsigned long const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned long, 4u>(unsigned long const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned __int128>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned __int128, 16u>(unsigned __int128 const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int const>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int const, 1u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int const>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int const, 2u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int const>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int const, 4u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned long const>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned long const, 8u>(unsigned long const&, unsigned long)
375
376
0
  uint8_t *const &getDataPtr() const noexcept { return DataPtr; }
377
0
  uint8_t *&getDataPtr() noexcept { return DataPtr; }
378
379
  /// Waiter support for atomic wait/notify across threads.
380
  struct Waiter {
381
    std::mutex Mutex;
382
    std::condition_variable Cond;
383
    bool Notified = false;
384
0
    Waiter() noexcept = default;
385
  };
386
387
0
  std::mutex &getWaiterMapMutex() noexcept { return WaiterMapMutex; }
388
389
0
  std::unordered_multimap<uint64_t, Waiter> &getWaiterMap() noexcept {
390
0
    return WaiterMap;
391
0
  }
392
393
  /// Wake all waiters on this memory instance (used by Executor::stop()).
394
0
  void notifyAllWaiters() noexcept {
395
0
    std::unique_lock<std::mutex> Locker(WaiterMapMutex);
396
0
    for (auto &[Addr, W] : WaiterMap) {
397
      // Lock the Waiter mutex and notify while holding it. This pairs with
398
      // the wait loop which checks StopToken under this same mutex before
399
      // entering Cond.wait(). The lock ensures we either:
400
      // (a) block until the waiter enters Cond.wait() — then wake it, or
401
      // (b) run before the waiter checks StopToken — it will see it set.
402
0
      std::unique_lock<std::mutex> WaiterLocker(W.Mutex);
403
0
      W.Cond.notify_all();
404
0
    }
405
0
  }
406
407
private:
408
  /// \name Data of memory instance.
409
  /// @{
410
  AST::MemoryType MemType;
411
  uint8_t *DataPtr = nullptr;
412
  uint64_t PageLimit;
413
  std::mutex WaiterMapMutex;
414
  std::unordered_multimap<uint64_t, Waiter> WaiterMap;
415
  /// @}
416
};
417
418
} // namespace Instance
419
} // namespace Runtime
420
} // namespace WasmEdge