Coverage Report

Created: 2026-11-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/include/runtime/instance/table.h
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
//===-- wasmedge/runtime/instance/table.h - Table Instance definition -----===//
5
//
6
// Part of the WasmEdge Project.
7
//
8
//===----------------------------------------------------------------------===//
9
///
10
/// \file
11
/// This file contains the table instance definition in store manager.
12
///
13
//===----------------------------------------------------------------------===//
14
#pragma once
15
16
#include "ast/segment.h"
17
#include "ast/type.h"
18
#include "common/errcode.h"
19
#include "common/errinfo.h"
20
#include "common/spdlog.h"
21
22
#include <algorithm>
23
#include <cstdint>
24
#include <vector>
25
26
namespace WasmEdge {
27
namespace Runtime {
28
namespace Instance {
29
30
class ModuleInstance;
31
32
class TableInstance {
33
public:
34
  TableInstance() = delete;
35
  TableInstance(const AST::TableType &TType) noexcept
36
0
      : TabType(TType),
37
0
        Refs(TType.getLimit().getMin(), RefVariant(TType.getRefType())),
38
0
        InitValue(RefVariant(TType.getRefType())),
39
0
        LiveSize(TType.getLimit().getMin()) {
40
    // The reference type should be nullable because there is no initial ref.
41
    // This constructor only handles abstract heap types correctly for null
42
    // refs. For concrete type indices, the caller should use the two-arg
43
    // constructor with a properly initialized RefVariant.
44
0
    assuming(TType.getRefType().isNullableRefType());
45
0
    assuming(TType.getRefType().isAbsHeapType());
46
0
    DataPtr = Refs.data();
47
0
  }
48
  TableInstance(const AST::TableType &TType, const RefVariant &InitVal) noexcept
49
0
      : TabType(TType), Refs(TType.getLimit().getMin(), InitVal),
50
0
        InitValue(InitVal), LiveSize(TType.getLimit().getMin()) {
51
    // If the reference type is not nullable, the initial reference is required.
52
0
    assuming(TType.getRefType().isNullableRefType() || !InitVal.isNull());
53
0
    DataPtr = Refs.data();
54
0
  }
55
  /// Constructor for a table instance defined by a module.
56
  TableInstance(const ModuleInstance *Mod, const AST::TableType &TType,
57
                const RefVariant &InitVal) noexcept
58
0
      : TableInstance(TType, InitVal) {
59
0
    ModInst = Mod;
60
0
  }
61
62
  /// Getter for the defining module instance, if any.
63
0
  const ModuleInstance *getModule() const noexcept { return ModInst; }
64
65
  /// Get size of table.refs
66
0
  uint64_t getSize() const noexcept {
67
    // The table size is bound to the limit in the table type.
68
0
    return TabType.getLimit().getMin();
69
0
  }
70
71
  /// Get a stable pointer to the live size field for compiled code.
72
0
  const uint64_t *getSizePtr() const noexcept { return &LiveSize; }
73
74
  /// Get the stable reference to the live element buffer for compiled code.
75
0
  RefVariant *const &getDataPtr() const noexcept { return DataPtr; }
76
0
  RefVariant *&getDataPtr() noexcept { return DataPtr; }
77
78
  /// Getter for table type.
79
0
  const AST::TableType &getTableType() const noexcept { return TabType; }
80
81
  /// Check whether access is out of bounds.
82
  bool checkAccessBound(const uint64_t Offset,
83
0
                        const uint64_t Length) const noexcept {
84
    // Due to applying the Memory64 proposal, we should avoid the overflow issue
85
    // of the following code:
86
    //   return Offset + Length <= Limit;
87
0
    const uint64_t Limit = TabType.getLimit().getMin();
88
0
    return std::numeric_limits<uint64_t>::max() - Offset >= Length &&
89
0
           Offset + Length <= Limit;
90
0
  }
91
92
  /// Grow table with initialization value.
93
0
  bool growTable(const uint64_t Count, const RefVariant &Val) noexcept {
94
0
    if (Count == 0) {
95
0
      return true;
96
0
    }
97
0
    uint64_t MaxSizeCaped = getMaxAddress(TabType.getLimit().getAddrType());
98
0
    const uint64_t Min = TabType.getLimit().getMin();
99
0
    assuming(MaxSizeCaped >= Min);
100
0
    if (TabType.getLimit().hasMax()) {
101
0
      const uint64_t Max = TabType.getLimit().getMax();
102
0
      MaxSizeCaped = std::min(Max, MaxSizeCaped);
103
0
    }
104
0
    if (Count > MaxSizeCaped - Min) {
105
0
      return false;
106
0
    }
107
0
    Refs.resize(Refs.size() + Count);
108
0
    std::fill_n(Refs.end() - static_cast<std::ptrdiff_t>(Count), Count, Val);
109
0
    DataPtr = Refs.data();
110
0
    setLiveSize(Min + Count);
111
0
    return true;
112
0
  }
113
0
  bool growTable(const uint64_t Count) noexcept {
114
0
    return growTable(Count, InitValue);
115
0
  }
116
117
  /// Shrink table.
118
0
  bool shrinkTable(const uint64_t Count) noexcept {
119
0
    if (Count > getSize()) {
120
0
      return false;
121
0
    }
122
0
    Refs.resize(getSize() - Count);
123
0
    DataPtr = Refs.data();
124
0
    setLiveSize(Refs.size());
125
0
    return true;
126
0
  }
127
128
  /// Get slice of Refs[Offset : Offset + Length - 1]
129
  Expect<Span<const RefVariant>> getRefs(const uint64_t Offset,
130
0
                                         const uint64_t Length) const noexcept {
131
    // Check the accessing boundary.
132
0
    if (!checkAccessBound(Offset, Length)) {
133
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
134
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
135
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
136
0
    }
137
0
    return Span<const RefVariant>(
138
0
        Refs.begin() + static_cast<std::ptrdiff_t>(Offset), Length);
139
0
  }
140
141
  /// Replace the Refs[Dst :] by Slice[Src : Src + Length)
142
  Expect<void> setRefs(Span<const RefVariant> Slice, const uint64_t Dst,
143
0
                       const uint64_t Src, const uint64_t Length) noexcept {
144
    // Check the accessing boundary.
145
0
    if (!checkAccessBound(Dst, Length)) {
146
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
147
0
      spdlog::error(ErrInfo::InfoBoundary(Dst, Length, getSize()));
148
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
149
0
    }
150
151
    // Check the input data validation.
152
0
    if (std::numeric_limits<uint64_t>::max() - Src < Length ||
153
0
        Src + Length > Slice.size()) {
154
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
155
0
      spdlog::error(ErrInfo::InfoBoundary(Src, Length, Slice.size()));
156
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
157
0
    }
158
159
    // Copy the references. The slice may be from the same table instance, so
160
    // use memmove semantics for the possible overlapping case.
161
0
    if (likely(Length > 0)) {
162
0
      std::memmove(Refs.data() + Dst, Slice.data() + Src,
163
0
                   Length * sizeof(RefVariant));
164
0
    }
165
0
    return {};
166
0
  }
167
168
  /// Fill the Refs[Offset : Offset + Length - 1] by Val.
169
  Expect<void> fillRefs(const RefVariant &Val, const uint64_t Offset,
170
0
                        const uint64_t Length) noexcept {
171
    // Check the accessing boundary.
172
0
    if (!checkAccessBound(Offset, Length)) {
173
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
174
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
175
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
176
0
    }
177
178
    // Fill the references.
179
0
    std::fill_n(Refs.begin() + static_cast<std::ptrdiff_t>(Offset), Length,
180
0
                Val);
181
0
    return {};
182
0
  }
183
184
  /// Get the elem address.
185
0
  Expect<RefVariant> getRefAddr(const uint64_t Idx) const noexcept {
186
0
    if (Idx >= Refs.size()) {
187
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
188
0
      spdlog::error(ErrInfo::InfoBoundary(Idx, 1, getSize()));
189
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
190
0
    }
191
0
    return Refs[Idx];
192
0
  }
193
194
  /// Set the elem address.
195
0
  Expect<void> setRefAddr(const uint64_t Idx, const RefVariant &Val) {
196
0
    if (Idx >= Refs.size()) {
197
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
198
0
      spdlog::error(ErrInfo::InfoBoundary(Idx, 1, getSize()));
199
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
200
0
    }
201
0
    Refs[Idx] = Val;
202
0
    return {};
203
0
  }
204
205
private:
206
  /// Update the size in the limit and its live mirror synchronously.
207
0
  void setLiveSize(const uint64_t Size) noexcept {
208
0
    TabType.getLimit().setMin(Size);
209
0
    LiveSize = Size;
210
0
  }
211
212
  /// \name Data of table instance.
213
  /// @{
214
  const ModuleInstance *ModInst = nullptr;
215
  AST::TableType TabType;
216
  std::vector<RefVariant> Refs;
217
  RefVariant InitValue;
218
  RefVariant *DataPtr = nullptr;
219
  uint64_t LiveSize;
220
  /// @}
221
};
222
223
} // namespace Instance
224
} // namespace Runtime
225
} // namespace WasmEdge