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