Coverage Report

Created: 2026-06-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/executor/engine/memoryInstr.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "executor/executor.h"
5
6
#include <cstring>
7
8
namespace WasmEdge {
9
namespace Executor {
10
11
Expect<void>
12
Executor::runMemorySizeOp(Runtime::StackManager &StackMgr,
13
0
                          Runtime::Instance::MemoryInstance &MemInst) {
14
  // Push SZ = page size to the stack.
15
0
  const auto AddrType = MemInst.getMemoryType().getLimit().getAddrType();
16
0
  StackMgr.push(emplaceAddr(MemInst.getPageSize(), AddrType));
17
0
  return {};
18
0
}
19
20
Expect<void>
21
Executor::runMemoryGrowOp(Runtime::StackManager &StackMgr,
22
0
                          Runtime::Instance::MemoryInstance &MemInst) {
23
  // Pop N, the number of pages to grow.
24
0
  const auto AddrType = MemInst.getMemoryType().getLimit().getAddrType();
25
0
  uint64_t N = extractAddr(StackMgr.pop(), AddrType);
26
27
  // Grow the page and push the result.
28
0
  const uint64_t CurrPageSize = MemInst.getPageSize();
29
0
  if (MemInst.growPage(N)) {
30
0
    StackMgr.push(emplaceAddr(CurrPageSize, AddrType));
31
0
  } else {
32
0
    StackMgr.push(emplaceAddr(static_cast<uint64_t>(-1), AddrType));
33
0
  }
34
0
  return {};
35
0
}
36
37
Expect<void> Executor::runMemoryInitOp(
38
    Runtime::StackManager &StackMgr, Runtime::Instance::MemoryInstance &MemInst,
39
0
    Runtime::Instance::DataInstance &DataInst, const AST::Instruction &Instr) {
40
  // Pop the length, source, and destination from the stack.
41
  // Currently, the length and source offset from the data instance are
42
  // 32-bit.
43
0
  uint64_t Len = static_cast<uint64_t>(StackMgr.pop().get<uint32_t>());
44
0
  uint64_t Src = static_cast<uint64_t>(StackMgr.pop().get<uint32_t>());
45
0
  const auto AddrType = MemInst.getMemoryType().getLimit().getAddrType();
46
0
  uint64_t Dst = extractAddr(StackMgr.pop(), AddrType);
47
48
  // Replace mem[Dst : Dst + Len] with data[Src : Src + Len].
49
0
  return MemInst.setBytes(DataInst.getData(), Dst, Src, Len)
50
0
      .map_error([&Instr](auto E) {
51
0
        spdlog::error(
52
0
            ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset()));
53
0
        return E;
54
0
      });
55
0
}
56
57
Expect<void>
58
0
Executor::runDataDropOp(Runtime::Instance::DataInstance &DataInst) {
59
  // Clear data instance.
60
0
  DataInst.clear();
61
0
  return {};
62
0
}
63
64
Expect<void>
65
Executor::runMemoryCopyOp(Runtime::StackManager &StackMgr,
66
                          Runtime::Instance::MemoryInstance &MemInstDst,
67
                          Runtime::Instance::MemoryInstance &MemInstSrc,
68
0
                          const AST::Instruction &Instr) {
69
  // Pop the length, source, and destination from the stack.
70
0
  const auto AddrType1 = MemInstSrc.getMemoryType().getLimit().getAddrType();
71
0
  const auto AddrType2 = MemInstDst.getMemoryType().getLimit().getAddrType();
72
0
  uint64_t Len = extractAddr(StackMgr.pop(), std::min(AddrType1, AddrType2));
73
0
  uint64_t Src = extractAddr(StackMgr.pop(), AddrType2);
74
0
  uint64_t Dst = extractAddr(StackMgr.pop(), AddrType1);
75
76
  // Replace mem[Dst : Dst + Len] with mem[Src : Src + Len].
77
  // When source and destination are the same memory instance, overlapping
78
  // regions require memmove semantics per the Wasm spec.
79
0
  if (&MemInstSrc == &MemInstDst) {
80
    // Same memory: validate bounds, then use memmove for overlap safety.
81
0
    EXPECTED_TRY(MemInstSrc.getBytes(Src, Len).map_error([&Instr](auto E) {
82
0
      spdlog::error(
83
0
          ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset()));
84
0
      return E;
85
0
    }));
86
0
    EXPECTED_TRY(MemInstDst.getBytes(Dst, Len).map_error([&Instr](auto E) {
87
0
      spdlog::error(
88
0
          ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset()));
89
0
      return E;
90
0
    }));
91
0
    if (likely(Len > 0)) {
92
0
      std::memmove(MemInstDst.getDataPtr() + Dst, MemInstSrc.getDataPtr() + Src,
93
0
                   Len);
94
0
    }
95
0
    return {};
96
0
  } else {
97
    // Different memories: no overlap possible, use the existing path.
98
0
    EXPECTED_TRY(auto Data,
99
0
                 MemInstSrc.getBytes(Src, Len).map_error([&Instr](auto E) {
100
0
                   spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(),
101
0
                                                          Instr.getOffset()));
102
0
                   return E;
103
0
                 }));
104
0
    return MemInstDst.setBytes(Data, Dst, 0, Len).map_error([&Instr](auto E) {
105
0
      spdlog::error(
106
0
          ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset()));
107
0
      return E;
108
0
    });
109
0
  }
110
0
}
111
112
Expect<void>
113
Executor::runMemoryFillOp(Runtime::StackManager &StackMgr,
114
                          Runtime::Instance::MemoryInstance &MemInst,
115
0
                          const AST::Instruction &Instr) {
116
  // Pop the length, value, and offset from the stack.
117
0
  const auto AddrType = MemInst.getMemoryType().getLimit().getAddrType();
118
0
  uint64_t Len = extractAddr(StackMgr.pop(), AddrType);
119
0
  uint8_t Val = static_cast<uint8_t>(StackMgr.pop().get<uint32_t>());
120
0
  uint64_t Off = extractAddr(StackMgr.pop(), AddrType);
121
122
  // Fill data with Val.
123
0
  return MemInst.fillBytes(Val, Off, Len).map_error([&Instr](auto E) {
124
0
    spdlog::error(
125
0
        ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset()));
126
0
    return E;
127
0
  });
128
0
}
129
130
} // namespace Executor
131
} // namespace WasmEdge