Coverage Report

Created: 2026-07-16 07:09

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 TableInstance {
31
public:
32
  TableInstance() = delete;
33
  TableInstance(const AST::TableType &TType) noexcept
34
0
      : TabType(TType),
35
0
        Refs(TType.getLimit().getMin(), RefVariant(TType.getRefType())),
36
0
        InitValue(RefVariant(TType.getRefType())),
37
0
        LiveSize(TType.getLimit().getMin()) {
38
    // The reference type should be nullable because there is no initial ref.
39
    // This constructor only handles abstract heap types correctly for null
40
    // refs. For concrete type indices, the caller should use the two-arg
41
    // constructor with a properly initialized RefVariant.
42
0
    assuming(TType.getRefType().isNullableRefType());
43
0
    assuming(TType.getRefType().isAbsHeapType());
44
0
    DataPtr = Refs.data();
45
0
  }
46
  TableInstance(const AST::TableType &TType, const RefVariant &InitVal) noexcept
47
0
      : TabType(TType), Refs(TType.getLimit().getMin(), InitVal),
48
0
        InitValue(InitVal), LiveSize(TType.getLimit().getMin()) {
49
    // If the reference type is not nullable, the initial reference is required.
50
0
    assuming(TType.getRefType().isNullableRefType() || !InitVal.isNull());
51
0
    DataPtr = Refs.data();
52
0
  }
53
54
  /// Get size of table.refs
55
0
  uint64_t getSize() const noexcept {
56
    // The table size is bound to the limit in the table type.
57
0
    return TabType.getLimit().getMin();
58
0
  }
59
60
  /// Get a stable pointer to the live size field for compiled code.
61
0
  const uint64_t *getSizePtr() const noexcept { return &LiveSize; }
62
63
  /// Get the stable reference to the live element buffer for compiled code.
64
0
  RefVariant *const &getDataPtr() const noexcept { return DataPtr; }
65
0
  RefVariant *&getDataPtr() noexcept { return DataPtr; }
66
67
  /// Getter for table type.
68
0
  const AST::TableType &getTableType() const noexcept { return TabType; }
69
70
  /// Check whether access is out of bounds.
71
  bool checkAccessBound(const uint64_t Offset,
72
0
                        const uint64_t Length) const noexcept {
73
    // Due to applying the Memory64 proposal, we should avoid the overflow issue
74
    // of the following code:
75
    //   return Offset + Length <= Limit;
76
0
    const uint64_t Limit = TabType.getLimit().getMin();
77
0
    return std::numeric_limits<uint64_t>::max() - Offset >= Length &&
78
0
           Offset + Length <= Limit;
79
0
  }
80
81
  /// Grow table with initialization value.
82
0
  bool growTable(const uint64_t Count, const RefVariant &Val) noexcept {
83
0
    if (Count == 0) {
84
0
      return true;
85
0
    }
86
0
    uint64_t MaxSizeCaped = getMaxAddress(TabType.getLimit().getAddrType());
87
0
    const uint64_t Min = TabType.getLimit().getMin();
88
0
    assuming(MaxSizeCaped >= Min);
89
0
    if (TabType.getLimit().hasMax()) {
90
0
      const uint64_t Max = TabType.getLimit().getMax();
91
0
      MaxSizeCaped = std::min(Max, MaxSizeCaped);
92
0
    }
93
0
    if (Count > MaxSizeCaped - Min) {
94
0
      return false;
95
0
    }
96
0
    Refs.resize(Refs.size() + Count);
97
0
    std::fill_n(Refs.end() - static_cast<std::ptrdiff_t>(Count), Count, Val);
98
0
    DataPtr = Refs.data();
99
0
    setLiveSize(Min + Count);
100
0
    return true;
101
0
  }
102
0
  bool growTable(const uint64_t Count) noexcept {
103
0
    return growTable(Count, InitValue);
104
0
  }
105
106
  /// Get slice of Refs[Offset : Offset + Length - 1]
107
  Expect<Span<const RefVariant>> getRefs(const uint64_t Offset,
108
0
                                         const uint64_t Length) const noexcept {
109
    // Check the accessing boundary.
110
0
    if (!checkAccessBound(Offset, Length)) {
111
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
112
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
113
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
114
0
    }
115
0
    return Span<const RefVariant>(
116
0
        Refs.begin() + static_cast<std::ptrdiff_t>(Offset), Length);
117
0
  }
118
119
  /// Replace the Refs[Dst :] by Slice[Src : Src + Length)
120
  Expect<void> setRefs(Span<const RefVariant> Slice, const uint64_t Dst,
121
0
                       const uint64_t Src, const uint64_t Length) noexcept {
122
    // Check the accessing boundary.
123
0
    if (!checkAccessBound(Dst, Length)) {
124
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
125
0
      spdlog::error(ErrInfo::InfoBoundary(Dst, Length, getSize()));
126
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
127
0
    }
128
129
    // Check the input data validation.
130
0
    if (std::numeric_limits<uint64_t>::max() - Src < Length ||
131
0
        Src + Length > Slice.size()) {
132
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
133
0
      spdlog::error(ErrInfo::InfoBoundary(Src, Length, Slice.size()));
134
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
135
0
    }
136
137
    // Copy the references. The slice may be from the same table instance, so
138
    // use memmove semantics for the possible overlapping case.
139
0
    if (likely(Length > 0)) {
140
0
      std::memmove(Refs.data() + Dst, Slice.data() + Src,
141
0
                   Length * sizeof(RefVariant));
142
0
    }
143
0
    return {};
144
0
  }
145
146
  /// Fill the Refs[Offset : Offset + Length - 1] by Val.
147
  Expect<void> fillRefs(const RefVariant &Val, const uint64_t Offset,
148
0
                        const uint64_t Length) noexcept {
149
    // Check the accessing boundary.
150
0
    if (!checkAccessBound(Offset, Length)) {
151
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
152
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
153
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
154
0
    }
155
156
    // Fill the references.
157
0
    std::fill_n(Refs.begin() + static_cast<std::ptrdiff_t>(Offset), Length,
158
0
                Val);
159
0
    return {};
160
0
  }
161
162
  /// Get the elem address.
163
0
  Expect<RefVariant> getRefAddr(const uint64_t Idx) const noexcept {
164
0
    if (Idx >= Refs.size()) {
165
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
166
0
      spdlog::error(ErrInfo::InfoBoundary(Idx, 1, getSize()));
167
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
168
0
    }
169
0
    return Refs[Idx];
170
0
  }
171
172
  /// Set the elem address.
173
0
  Expect<void> setRefAddr(const uint64_t Idx, const RefVariant &Val) {
174
0
    if (Idx >= Refs.size()) {
175
0
      spdlog::error(ErrCode::Value::TableOutOfBounds);
176
0
      spdlog::error(ErrInfo::InfoBoundary(Idx, 1, getSize()));
177
0
      return Unexpect(ErrCode::Value::TableOutOfBounds);
178
0
    }
179
0
    Refs[Idx] = Val;
180
0
    return {};
181
0
  }
182
183
private:
184
  /// Update the size in the limit and its live mirror synchronously.
185
0
  void setLiveSize(const uint64_t Size) noexcept {
186
0
    TabType.getLimit().setMin(Size);
187
0
    LiveSize = Size;
188
0
  }
189
190
  /// \name Data of table instance.
191
  /// @{
192
  AST::TableType TabType;
193
  std::vector<RefVariant> Refs;
194
  RefVariant InitValue;
195
  RefVariant *DataPtr = nullptr;
196
  uint64_t LiveSize;
197
  /// @}
198
};
199
200
} // namespace Instance
201
} // namespace Runtime
202
} // namespace WasmEdge