Coverage Report

Created: 2026-08-14 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/include/runtime/stackmgr.h
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
//===-- wasmedge/runtime/stackmgr.h - Stack Manager definition ------------===//
5
//
6
// Part of the WasmEdge Project.
7
//
8
//===----------------------------------------------------------------------===//
9
///
10
/// \file
11
/// This file contains the definition of Stack Manager.
12
///
13
//===----------------------------------------------------------------------===//
14
#pragma once
15
16
#include "ast/instruction.h"
17
#include "runtime/instance/module.h"
18
19
#include <optional>
20
#include <vector>
21
22
namespace WasmEdge {
23
namespace Runtime {
24
25
class StackManager {
26
public:
27
  using Value = ValVariant;
28
29
  struct Handler {
30
    Handler(AST::InstrView::iterator TryIt, uint32_t V,
31
            Span<const AST::Instruction::CatchDescriptor> C)
32
0
        : Try(TryIt), VPos(V), CatchClause(C) {}
33
    AST::InstrView::iterator Try;
34
    uint32_t VPos;
35
    Span<const AST::Instruction::CatchDescriptor> CatchClause;
36
  };
37
38
  struct Frame {
39
    Frame() = delete;
40
    Frame(const Instance::ModuleInstance *Mod, AST::InstrView::iterator FromIt,
41
          uint32_t L, uint32_t A, uint32_t V, bool E) noexcept
42
0
        : Module(Mod), From(FromIt), Locals(L), Arity(A), VPos(V),
43
0
          NativeEntry(E) {}
44
    const Instance::ModuleInstance *Module;
45
    AST::InstrView::iterator From;
46
    uint32_t Locals;
47
    uint32_t Arity;
48
    uint32_t VPos;
49
    bool NativeEntry;
50
    std::vector<Handler> HandlerStack;
51
  };
52
53
  /// Stack manager provides the stack control for Wasm execution with VALIDATED
54
  /// modules. All operations of instructions passed validation, therefore no
55
  /// unexpect operations will occur.
56
0
  StackManager() noexcept {
57
0
    ValueStack.reserve(2048U);
58
0
    FrameStack.reserve(16U);
59
0
  }
60
0
  ~StackManager() = default;
61
62
  /// Getter for stack size.
63
0
  size_t size() const noexcept { return ValueStack.size(); }
64
65
  /// Unsafe getter for the top entry of the stack.
66
0
  Value &getTop() { return ValueStack.back(); }
67
68
  /// Unsafe getter for the N-th value entry from the top of the stack.
69
0
  Value &getTopN(uint32_t Offset) noexcept {
70
0
    assuming(0 < Offset && Offset <= ValueStack.size());
71
0
    return ValueStack[ValueStack.size() - Offset];
72
0
  }
73
74
  /// Unsafe getter for the top N value entries of the stack.
75
0
  Span<Value> getTopSpan(uint32_t N) {
76
0
    return Span<Value>(ValueStack.end() - N, N);
77
0
  }
78
79
  /// Push a new value entry to the stack.
80
0
  template <typename T> void push(T &&Val) {
81
0
    ValueStack.push_back(std::forward<T>(Val));
82
0
  }
Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant> const&>(WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant> const&)
Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<WasmEdge::RefVariant>(WasmEdge::RefVariant&&)
Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant> >(WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant>&&)
Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant>&>(WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant>&)
Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<WasmEdge::RefVariant&>(WasmEdge::RefVariant&)
83
84
  /// Push a vector of values to the stack.
85
0
  void pushValVec(const std::vector<Value> &ValVec) {
86
0
    ValueStack.insert(ValueStack.end(), ValVec.begin(), ValVec.end());
87
0
  }
88
89
  /// Unsafe pop and return the top entry.
90
0
  Value pop() {
91
0
    Value V = std::move(ValueStack.back());
92
0
    ValueStack.pop_back();
93
0
    return V;
94
0
  }
95
96
  /// Unsafe pop and return the top N entries.
97
0
  std::vector<Value> pop(uint32_t N) {
98
0
    std::vector<Value> Vec;
99
0
    Vec.reserve(N);
100
0
    std::move(ValueStack.end() - N, ValueStack.end(), std::back_inserter(Vec));
101
0
    ValueStack.erase(ValueStack.end() - N, ValueStack.end());
102
0
    return Vec;
103
0
  }
104
105
  /// Push a new frame entry to the stack. Set `IsNativeEntry` for the frames
106
  /// entered from the native code, which the exception handler walk never
107
  /// crosses; tail calls inherit the flag from the replaced frame.
108
  void pushFrame(const Instance::ModuleInstance *Module,
109
                 AST::InstrView::iterator From, uint32_t LocalNum = 0,
110
                 uint32_t Arity = 0, bool IsTailCall = false,
111
0
                 bool IsNativeEntry = false) noexcept {
112
0
    if (!IsTailCall) {
113
0
      FrameStack.emplace_back(Module, From, LocalNum, Arity,
114
0
                              static_cast<uint32_t>(ValueStack.size()),
115
0
                              IsNativeEntry);
116
0
    } else {
117
0
      assuming(!FrameStack.empty());
118
0
      assuming(FrameStack.back().VPos >= FrameStack.back().Locals);
119
0
      assuming(FrameStack.back().VPos - FrameStack.back().Locals <=
120
0
               ValueStack.size() - LocalNum);
121
0
      ValueStack.erase(ValueStack.begin() + FrameStack.back().VPos -
122
0
                           FrameStack.back().Locals,
123
0
                       ValueStack.end() - LocalNum);
124
0
      FrameStack.back().Module = Module;
125
0
      FrameStack.back().Locals = LocalNum;
126
0
      FrameStack.back().Arity = Arity;
127
0
      FrameStack.back().VPos = static_cast<uint32_t>(ValueStack.size());
128
0
      FrameStack.back().HandlerStack.clear();
129
0
    }
130
0
  }
131
132
  /// Unsafe pop top frame.
133
0
  AST::InstrView::iterator popFrame() noexcept {
134
0
    assuming(!FrameStack.empty());
135
0
    assuming(FrameStack.back().VPos >= FrameStack.back().Locals);
136
0
    assuming(FrameStack.back().VPos - FrameStack.back().Locals <=
137
0
             ValueStack.size() - FrameStack.back().Arity);
138
0
    ValueStack.erase(ValueStack.begin() + FrameStack.back().VPos -
139
0
                         FrameStack.back().Locals,
140
0
                     ValueStack.end() - FrameStack.back().Arity);
141
0
    auto From = FrameStack.back().From;
142
0
    FrameStack.pop_back();
143
0
    return From;
144
0
  }
145
146
  // Get all frames
147
0
  Span<const Frame> getFramesSpan() const { return FrameStack; }
148
149
  /// Getter of the native entry flag of the top frame.
150
0
  bool isTopFrameNativeEntry() const noexcept {
151
0
    return !FrameStack.empty() && FrameStack.back().NativeEntry;
152
0
  }
153
154
  /// Push handler for try-catch block.
155
  void
156
  pushHandler(AST::InstrView::iterator TryIt, uint32_t BlockParamNum,
157
0
              Span<const AST::Instruction::CatchDescriptor> Catch) noexcept {
158
0
    assuming(!FrameStack.empty());
159
0
    FrameStack.back().HandlerStack.emplace_back(
160
0
        TryIt, static_cast<uint32_t>(ValueStack.size()) - BlockParamNum, Catch);
161
0
  }
162
163
  /// Pop the top handler on the stack. The walk stops without popping when
164
  /// reaching a frame entered from the native code.
165
0
  std::optional<Handler> popTopHandler(uint32_t AssocValSize) noexcept {
166
0
    while (!FrameStack.empty()) {
167
0
      auto &Frame = FrameStack.back();
168
0
      if (!Frame.HandlerStack.empty()) {
169
0
        auto TopHandler = std::move(Frame.HandlerStack.back());
170
0
        Frame.HandlerStack.pop_back();
171
0
        assuming(TopHandler.VPos <= ValueStack.size() - AssocValSize);
172
0
        ValueStack.erase(ValueStack.begin() + TopHandler.VPos,
173
0
                         ValueStack.end() - AssocValSize);
174
0
        return TopHandler;
175
0
      }
176
0
      if (Frame.NativeEntry) {
177
0
        break;
178
0
      }
179
0
      FrameStack.pop_back();
180
0
    }
181
0
    return std::nullopt;
182
0
  }
183
184
  /// Unsafe remove inactive handler.
185
0
  void removeInactiveHandler(AST::InstrView::iterator PC) noexcept {
186
0
    assuming(!FrameStack.empty());
187
    // Pop the handlers the branch left behind. Callers must run this while the
188
    // inactive handlers are still on top: a handler buried under a later
189
    // try_table is indistinguishable from an active one here, so branchToLabel
190
    // cleans up before any new handler can be pushed over the stale ones.
191
0
    auto &HandlerStack = FrameStack.back().HandlerStack;
192
0
    while (!HandlerStack.empty()) {
193
0
      auto &Handler = HandlerStack.back();
194
0
      if (PC < Handler.Try ||
195
0
          PC > Handler.Try + Handler.Try->getTryCatch().JumpEnd) {
196
0
        HandlerStack.pop_back();
197
0
      } else {
198
0
        break;
199
0
      }
200
0
    }
201
0
  }
202
203
  /// Unsafe erase value stack.
204
0
  void eraseValueStack(uint32_t EraseBegin, uint32_t EraseEnd) noexcept {
205
0
    assuming(EraseEnd <= EraseBegin && EraseBegin <= ValueStack.size());
206
0
    ValueStack.erase(ValueStack.end() - EraseBegin,
207
0
                     ValueStack.end() - EraseEnd);
208
0
  }
209
210
  // Get all Value
211
0
  Span<const Value> getValueSpan() const { return ValueStack; }
212
213
  /// Unsafe leave top label.
214
  AST::InstrView::iterator
215
0
  maybePopFrameOrHandler(AST::InstrView::iterator PC) noexcept {
216
0
    if (FrameStack.size() > 1 && PC->isExprLast()) {
217
      // Noted that there's always a base frame in stack.
218
0
      return popFrame();
219
0
    }
220
0
    if (PC->isTryBlockLast()) {
221
0
      FrameStack.back().HandlerStack.pop_back();
222
0
    }
223
0
    return PC;
224
0
  }
225
226
  /// Unsafe getter of module address.
227
0
  const Instance::ModuleInstance *getModule() const noexcept {
228
0
    if (unlikely(FrameStack.empty())) {
229
0
      return nullptr;
230
0
    }
231
0
    return FrameStack.back().Module;
232
0
  }
233
234
  /// Reset stack.
235
0
  void reset() noexcept {
236
0
    ValueStack.clear();
237
0
    FrameStack.clear();
238
0
  }
239
240
private:
241
  /// \name Data of the stack manager.
242
  /// @{
243
  std::vector<Value> ValueStack;
244
  std::vector<Frame> FrameStack;
245
  /// @}
246
};
247
248
} // namespace Runtime
249
} // namespace WasmEdge