Coverage Report

Created: 2026-11-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/loader/ast/segment.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "loader/loader.h"
5
6
namespace WasmEdge {
7
namespace Loader {
8
9
// Load binary of TableSegment node. See "include/loader/loader.h".
10
10.6k
Expect<void> Loader::loadSegment(AST::TableSegment &TabSeg) {
11
  // Check whether the first byte is the reftype in table type.
12
10.6k
  EXPECTED_TRY(uint8_t CheckByte, FMgr.peekByte().map_error([this](auto E) {
13
10.6k
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Seg_Table);
14
10.6k
  }));
15
16
10.6k
  if (CheckByte == 0x40U) {
17
    // Table segment case is for FunctionReferences proposal.
18
1.14k
    if (!Conf.hasProposal(Proposal::FunctionReferences)) {
19
0
      return logNeedProposal(ErrCode::Value::MalformedTable,
20
0
                             Proposal::FunctionReferences, FMgr.getLastOffset(),
21
0
                             ASTNodeAttr::Seg_Table);
22
0
    }
23
1.14k
    FMgr.readByte();
24
25
    // Check the second byte.
26
1.14k
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error([this](auto E) {
27
1.13k
      return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Seg_Table);
28
1.13k
    }));
29
1.13k
    if (B != 0x00U) {
30
8
      return logLoadError(ErrCode::Value::MalformedTable, FMgr.getLastOffset(),
31
8
                          ASTNodeAttr::Seg_Table);
32
8
    }
33
34
    // Read the table type.
35
1.13k
    EXPECTED_TRY(loadType(TabSeg.getTableType()).map_error([](auto E) {
36
1.12k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Table));
37
1.12k
      return E;
38
1.12k
    }));
39
40
    // Read the expression.
41
1.12k
    EXPECTED_TRY(loadExpression(TabSeg.getExpr()).map_error([](auto E) {
42
1.12k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Table));
43
1.12k
      return E;
44
1.12k
    }));
45
9.51k
  } else {
46
    // The table type case.
47
9.51k
    EXPECTED_TRY(loadType(TabSeg.getTableType()).map_error([](auto E) {
48
9.51k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Table));
49
9.51k
      return E;
50
9.51k
    }));
51
9.51k
  }
52
53
10.4k
  return {};
54
10.6k
}
55
56
// Load binary of GlobalSegment node. See "include/loader/loader.h".
57
4.56k
Expect<void> Loader::loadSegment(AST::GlobalSegment &GlobSeg) {
58
4.56k
  return Expect<void>{}
59
4.56k
      .and_then([this, &GlobSeg]() {
60
        // Read global type node.
61
4.56k
        return loadType(GlobSeg.getGlobalType());
62
4.56k
      })
63
4.56k
      .and_then([this, &GlobSeg]() {
64
        // Read the expression.
65
4.44k
        return loadExpression(GlobSeg.getExpr());
66
4.44k
      })
67
4.56k
      .map_error([](auto E) {
68
989
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Global));
69
989
        return E;
70
989
      });
71
4.56k
}
72
73
// Load binary of ElementSegment node. See "include/loader/loader.h".
74
15.5k
Expect<void> Loader::loadSegment(AST::ElementSegment &ElemSeg) {
75
15.5k
  auto ReportError = [this](auto E) {
76
150
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Seg_Element);
77
150
  };
78
79
  // Element segment binary format:
80
  // ---------------------------------------------------------------------------
81
  //  Mode | TableIdx | OffExpr | ElemKind | RefType | vec(FuncIdx) | vec(expr)
82
  // ------|----------|---------|----------|---------|--------------|-----------
83
  //    0  |          |    v    |          |         |       v      |
84
  //    1  |          |         |    v     |         |       v      |
85
  //    2  |    v     |    v    |    v     |         |       v      |
86
  //    3  |          |         |    v     |         |       v      |
87
  //    4  |          |    v    |          |         |              |     v
88
  //    5  |          |         |          |    v    |              |     v
89
  //    6  |    v     |    v    |          |    v    |              |     v
90
  //    7  |          |         |          |    v    |              |     v
91
  // ---------------------------------------------------------------------------
92
  // Mode: element initial integer, u32
93
  // TableIdx: target table index, u32
94
  // OffExpr: init offset expression, expr
95
  // ElemKind: byte 0x00, ref.func
96
  // RefType: reference type, RefType
97
  // vec(FuncIdx): function index vector, vec(u32)
98
  // vec(expr): reference init list, vec(expr)
99
100
  // Read the checking byte.
101
15.5k
  uint32_t Check = 0;
102
15.5k
  if (unlikely(!Conf.hasProposal(Proposal::BulkMemoryOperations) &&
103
15.5k
               !Conf.hasProposal(Proposal::ReferenceTypes))) {
104
    // Legacy for BulkMemoryOperations and ReferenceTypes proposals turned off.
105
    // Element segment binary format: TableIdx + OffExpr + vec(FuncIdx)
106
0
    EXPECTED_TRY(FMgr.readU32().map_error(ReportError).map([&](auto Idx) {
107
0
      ElemSeg.setIdx(Idx);
108
0
    }));
109
15.5k
  } else {
110
15.5k
    EXPECTED_TRY(Check, FMgr.readU32().map_error(ReportError));
111
15.5k
  }
112
113
  // Check the prefix byte.
114
15.5k
  switch (Check) {
115
6.67k
  case 0x00:
116
9.04k
  case 0x02:
117
11.1k
  case 0x04:
118
11.7k
  case 0x06:
119
11.7k
    ElemSeg.setMode(AST::ElementSegment::ElemMode::Active);
120
11.7k
    break;
121
122
1.03k
  case 0x01:
123
2.01k
  case 0x05:
124
2.01k
    ElemSeg.setMode(AST::ElementSegment::ElemMode::Passive);
125
2.01k
    break;
126
127
1.30k
  case 0x03:
128
1.61k
  case 0x07:
129
1.61k
    ElemSeg.setMode(AST::ElementSegment::ElemMode::Declarative);
130
1.61k
    break;
131
132
109
  default:
133
    // TODO: Correct the error code once there's spec test.
134
109
    return logLoadError(ErrCode::Value::IllegalGrammar, FMgr.getLastOffset(),
135
109
                        ASTNodeAttr::Seg_Element);
136
15.5k
  }
137
138
  // Read the table index.
139
15.4k
  ElemSeg.setIdx(0);
140
15.4k
  switch (Check) {
141
2.37k
  case 0x02:
142
2.96k
  case 0x06:
143
2.96k
    EXPECTED_TRY(FMgr.readU32().map_error(ReportError).map([&](auto Idx) {
144
2.95k
      ElemSeg.setIdx(Idx);
145
2.95k
    }));
146
2.95k
    break;
147
148
12.4k
  default:
149
12.4k
    break;
150
15.4k
  }
151
152
  // Read the expression.
153
15.3k
  switch (Check) {
154
6.67k
  case 0x00:
155
9.03k
  case 0x02:
156
11.1k
  case 0x04:
157
11.7k
  case 0x06:
158
11.7k
    EXPECTED_TRY(loadExpression(ElemSeg.getExpr()).map_error([](auto E) {
159
8.43k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Element));
160
8.43k
      return E;
161
8.43k
    }));
162
8.43k
    break;
163
164
8.43k
  default:
165
3.62k
    break;
166
15.3k
  }
167
168
  // Read element kind and init function indices.
169
12.0k
  switch (Check) {
170
1.03k
  case 0x01:
171
3.34k
  case 0x02:
172
4.64k
  case 0x03:
173
4.64k
    EXPECTED_TRY(FMgr.readByte()
174
4.60k
                     .and_then([&](auto B) -> Expect<void> {
175
4.60k
                       if (B != 0x00U) {
176
4.60k
                         return Unexpect(ErrCode::Value::ExpectedZeroByte);
177
4.60k
                       };
178
4.60k
                       return {};
179
4.60k
                     })
180
4.60k
                     .map_error(ReportError));
181
4.60k
    [[fallthrough]];
182
183
8.43k
  case 0x00: {
184
8.43k
    EXPECTED_TRY(uint32_t VecCnt, loadVecCnt().map_error(ReportError));
185
544k
    for (uint32_t I = 0; I < VecCnt; ++I) {
186
      // For each element in vec(funcidx), make expr(ref.func idx end).
187
536k
      ElemSeg.getInitExprs().emplace_back();
188
536k
      AST::Instruction RefFunc(OpCode::Ref__func);
189
536k
      AST::Instruction End(OpCode::End);
190
536k
      EXPECTED_TRY(loadInstruction(RefFunc).map_error([](auto E) {
191
536k
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Element));
192
536k
        return E;
193
536k
      }));
194
536k
      ElemSeg.getInitExprs().back().getInstrs().emplace_back(
195
536k
          std::move(RefFunc));
196
536k
      ElemSeg.getInitExprs().back().getInstrs().emplace_back(std::move(End));
197
536k
    }
198
8.29k
    break;
199
8.36k
  }
200
8.29k
  default:
201
3.58k
    break;
202
12.0k
  }
203
204
  // Set the default reference type.
205
11.8k
  if (Check == 0x04) {
206
1.75k
    ElemSeg.setRefType(TypeCode::FuncRef);
207
10.1k
  } else {
208
10.1k
    ElemSeg.setRefType(ValType(TypeCode::Ref, TypeCode::FuncRef));
209
10.1k
  }
210
211
  // Read the reference type and init expressions.
212
11.8k
  switch (Check) {
213
976
  case 0x05:
214
1.51k
  case 0x06:
215
1.82k
  case 0x07: {
216
    // The AST node information is handled.
217
1.82k
    EXPECTED_TRY(auto Type, loadRefType(ASTNodeAttr::Seg_Element));
218
1.81k
    ElemSeg.setRefType(Type);
219
1.81k
    [[fallthrough]];
220
1.81k
  }
221
3.56k
  case 0x04: {
222
3.56k
    return loadVec<AST::ElementSegment>(
223
5.83k
        ElemSeg.getInitExprs(), [this](AST::Expression &Expr) -> Expect<void> {
224
5.83k
          return loadExpression(Expr);
225
5.83k
        });
226
1.81k
  }
227
228
8.29k
  default:
229
8.29k
    break;
230
11.8k
  }
231
232
8.29k
  return {};
233
11.8k
}
234
235
// Load binary of CodeSegment node. See "include/loader/loader.h".
236
33.3k
Expect<void> Loader::loadSegment(AST::CodeSegment &CodeSeg) {
237
33.3k
  auto ReportError = [this](auto E) {
238
101
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Seg_Code);
239
101
  };
240
241
  // Read the code segment size.
242
33.3k
  EXPECTED_TRY(FMgr.readU32().map_error(ReportError).map([&](auto S) {
243
33.3k
    CodeSeg.setSegSize(S);
244
33.3k
  }));
245
33.3k
  auto ExprSizeBound = FMgr.getOffset() + CodeSeg.getSegSize();
246
247
  // Read the vector of local variable counts and types.
248
33.3k
  EXPECTED_TRY(uint32_t VecCnt, loadVecCnt().map_error(ReportError));
249
33.3k
  CodeSeg.getLocals().clear();
250
33.3k
  CodeSeg.getLocals().reserve(VecCnt);
251
33.3k
  uint32_t TotalLocalCnt = 0;
252
64.6k
  for (uint32_t I = 0; I < VecCnt; ++I) {
253
31.4k
    EXPECTED_TRY(uint32_t LocalCnt, FMgr.readU32().map_error(ReportError));
254
    // Total local variables should not exceed 2^32. Capped at 2^26.
255
31.4k
    if (UINT32_C(67108864) - TotalLocalCnt < LocalCnt) {
256
21
      return logLoadError(ErrCode::Value::TooManyLocals, FMgr.getLastOffset(),
257
21
                          ASTNodeAttr::Seg_Code);
258
21
    }
259
31.4k
    TotalLocalCnt += LocalCnt;
260
    // Read the value type.
261
    // The AST node information is handled.
262
31.4k
    EXPECTED_TRY(ValType LocalType, loadValType(ASTNodeAttr::Seg_Code));
263
31.3k
    CodeSeg.getLocals().push_back(std::make_pair(LocalCnt, LocalType));
264
31.3k
  }
265
266
33.1k
  if (Conf.getRuntimeConfigure().getRunMode() == RunMode::AOT &&
267
0
      WASMType != InputType::WASM) {
268
    // In AOT run mode with an AOT artifact, skip parsing the function body.
269
0
    FMgr.seek(ExprSizeBound);
270
33.1k
  } else {
271
    // Read function body with expected expression size.
272
33.1k
    EXPECTED_TRY(
273
33.1k
        loadExpression(CodeSeg.getExpr(), ExprSizeBound).map_error([](auto E) {
274
33.1k
          spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Code));
275
33.1k
          return E;
276
33.1k
        }));
277
33.1k
  }
278
279
31.5k
  return {};
280
33.1k
}
281
282
// Load binary of DataSegment node. See "include/loader/loader.h".
283
11.5k
Expect<void> Loader::loadSegment(AST::DataSegment &DataSeg) {
284
11.5k
  auto ReportError = [this](auto E) {
285
134
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Seg_Data);
286
134
  };
287
11.5k
  DataSeg.setMode(AST::DataSegment::DataMode::Passive);
288
11.5k
  DataSeg.setIdx(0);
289
290
  // Data segment binary format:
291
  // ----------------------------------------
292
  //  Mode | MemoryIdx | OffExpr | vec(byte)
293
  // ------|-----------|---------|-----------
294
  //    0  |           |    v    |     v
295
  //    1  |           |         |     v
296
  //    2  |     v     |    v    |     v
297
  // ----------------------------------------
298
  // Mode: data initial integer, u32
299
  // MemoryIdx: target memory index, u32
300
  // OffExpr: init offset expression, expr
301
  // vec(byte): init data, vec(u8)
302
303
  // Read the checking byte.
304
11.5k
  EXPECTED_TRY(uint32_t Check, FMgr.readU32().map_error(ReportError));
305
  // Check > 0 cases are for BulkMemoryOperations or ReferenceTypes proposal.
306
11.5k
  if (Check > 0 && !Conf.hasProposal(Proposal::BulkMemoryOperations) &&
307
0
      !Conf.hasProposal(Proposal::ReferenceTypes)) {
308
0
    return logNeedProposal(ErrCode::Value::ExpectedZeroByte,
309
0
                           Proposal::BulkMemoryOperations, FMgr.getLastOffset(),
310
0
                           ASTNodeAttr::Seg_Data);
311
0
  }
312
313
11.5k
  switch (Check) {
314
918
  case 0x02: // 0x02 memidx expr vec(byte) , Active
315
    // Read target memory index.
316
918
    EXPECTED_TRY(FMgr.readU32().map_error(ReportError).map([&](auto Idx) {
317
914
      DataSeg.setIdx(Idx);
318
914
    }));
319
914
    [[fallthrough]];
320
321
5.33k
  case 0x00: // 0x00 expr vec(byte) , Active
322
    // Read the offset expression.
323
5.33k
    EXPECTED_TRY(loadExpression(DataSeg.getExpr()).map_error([](auto E) {
324
2.38k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Data));
325
2.38k
      return E;
326
2.38k
    }));
327
2.38k
    DataSeg.setMode(AST::DataSegment::DataMode::Active);
328
2.38k
    [[fallthrough]];
329
330
8.48k
  case 0x01: // 0x01 vec(byte) , Passive
331
8.48k
  {
332
    // Read initialization data.
333
8.48k
    EXPECTED_TRY(uint32_t VecCnt, loadVecCnt().map_error(ReportError));
334
8.39k
    EXPECTED_TRY(FMgr.readBytes(VecCnt).map_error(ReportError).map([&](auto V) {
335
8.38k
      DataSeg.getData() = std::move(V);
336
8.38k
    }));
337
8.38k
    break;
338
8.39k
  }
339
8.38k
  default:
340
    // TODO: Correct the error code once there's spec test.
341
109
    return logLoadError(ErrCode::Value::IllegalGrammar, FMgr.getLastOffset(),
342
109
                        ASTNodeAttr::Seg_Data);
343
11.5k
  }
344
8.38k
  return {};
345
11.5k
}
346
347
} // namespace Loader
348
} // namespace WasmEdge