Coverage Report

Created: 2026-09-28 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/loader/ast/component/component_canonical.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
68.9k
Expect<void> Loader::loadCanonical(AST::Component::Canonical &C) {
10
68.9k
  auto ReportError = [this](auto E) {
11
337
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_Canonical);
12
337
  };
component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonical(WasmEdge::AST::Component::Canonical&)::$_0::operator()<WasmEdge::ErrCode>(WasmEdge::ErrCode) const
Line
Count
Source
10
242
  auto ReportError = [this](auto E) {
11
242
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_Canonical);
12
242
  };
component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonical(WasmEdge::AST::Component::Canonical&)::$_0::operator()<WasmEdge::ErrCode::Value>(WasmEdge::ErrCode::Value) const
Line
Count
Source
10
95
  auto ReportError = [this](auto E) {
11
95
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_Canonical);
12
95
  };
13
  // canon ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx>
14
  //           => (canon lift f opts type-index-space[ft])
15
  //         | 0x01 0x00 f:<funcidx> opts:<opts>
16
  //           => (canon lower f opts (core func))
17
  //         | 0x02 rt:<typeidx>    => (canon resource.new rt (core func))
18
  //         | 0x03 rt:<typeidx>    => (canon resource.drop rt (core func))
19
  //         | 0x04 rt:<typeidx>    => (canon resource.rep rt (core func))
20
  //         | 0x09 rs:<resultlist> opts:<opts>
21
  //           => (canon task.return rs opts (core func)) ๐Ÿ”€
22
  //         | 0x05                 => (canon task.cancel (core func)) ๐Ÿ”€
23
  //         | 0x0a t:<core:valtype> i:<u32>
24
  //           => (canon context.get t i (core func)) ๐Ÿ”€
25
  //         | 0x0b t:<core:valtype> i:<u32>
26
  //           => (canon context.set t i (core func)) ๐Ÿ”€
27
  //         | 0x0c cancel?:<cancel?>
28
  //           => (canon thread.yield cancel? (core func)) ๐Ÿ”€
29
  //         | 0x06 async?:<async?>
30
  //           => (canon subtask.cancel async? (core func)) ๐Ÿ”€
31
  //         | 0x0d                 => (canon subtask.drop (core func)) ๐Ÿ”€
32
  //         | 0x0e t:<typeidx>     => (canon stream.new t (core func)) ๐Ÿ”€
33
  //         | 0x0f t:<typeidx> opts:<opts>
34
  //           => (canon stream.read t opts (core func)) ๐Ÿ”€
35
  //         | 0x10 t:<typeidx> opts:<opts>
36
  //           => (canon stream.write t opts (core func)) ๐Ÿ”€
37
  //         | 0x11 t:<typeidx> async?:<async?>
38
  //           => (canon stream.cancel-read t async? (core func)) ๐Ÿ”€
39
  //         | 0x12 t:<typeidx> async?:<async?>
40
  //           => (canon stream.cancel-write t async? (core func)) ๐Ÿ”€
41
  //         | 0x13 t:<typeidx>
42
  //           => (canon stream.drop-readable t (core func)) ๐Ÿ”€
43
  //         | 0x14 t:<typeidx>
44
  //           => (canon stream.drop-writable t (core func)) ๐Ÿ”€
45
  //         | 0x15 t:<typeidx> => (canon future.new t (core func)) ๐Ÿ”€
46
  //         | 0x16 t:<typeidx> opts:<opts>
47
  //           => (canon future.read t opts (core func)) ๐Ÿ”€
48
  //         | 0x17 t:<typeidx> opts:<opts>
49
  //           => (canon future.write t opts (core func)) ๐Ÿ”€
50
  //         | 0x18 t:<typeidx> async?:<async?>
51
  //           => (canon future.cancel-read t async? (core func)) ๐Ÿ”€
52
  //         | 0x19 t:<typeidx> async?:<async?>
53
  //           => (canon future.cancel-write t async? (core func)) ๐Ÿ”€
54
  //         | 0x1a t:<typeidx>
55
  //           => (canon future.drop-readable t (core func)) ๐Ÿ”€
56
  //         | 0x1b t:<typeidx>
57
  //           => (canon future.drop-writable t (core func)) ๐Ÿ”€
58
  //         | 0x1c opts:<opts> => (canon error-context.new opts (core func)) ๐Ÿ“
59
  //         | 0x1d opts:<opts>
60
  //           => (canon error-context.debug-message opts (core func)) ๐Ÿ“
61
  //         | 0x1e                => (canon error-context.drop (core func)) ๐Ÿ“
62
  //         | 0x1f                => (canon waitable-set.new (core func)) ๐Ÿ”€
63
  //         | 0x20 cancel?:<cancel?> m:<core:memoryidx>
64
  //           => (canon waitable-set.wait cancel? (memory m) (core func)) ๐Ÿ”€
65
  //         | 0x21 cancel?:<cancel?> m:<core:memoryidx>
66
  //           => (canon waitable-set.poll cancel? (memory m) (core func)) ๐Ÿ”€
67
  //         | 0x22                => (canon waitable-set.drop (core func)) ๐Ÿ”€
68
  //         | 0x23                => (canon waitable.join (core func)) ๐Ÿ”€
69
  //         | 0x24                => (canon backpressure.inc (core func)) ๐Ÿ”€
70
  //         | 0x25                => (canon backpressure.dec (core func)) ๐Ÿ”€
71
  //         | 0x26                => (canon thread.index (core func)) ๐Ÿงต
72
  //         | 0x27 ft:<core:typeidx> tbl:<core:tableidx>
73
  //           => (canon thread.new-indirect ft tbl (core func)) ๐Ÿงต
74
  //         | 0x28                => (canon thread.resume-later (core func)) ๐Ÿงต
75
  //         | 0x29 cancel?:<cancel?>
76
  //           => (canon thread.suspend cancel? (core func)) ๐Ÿงต
77
  //         | 0x2a cancel?:<cancel?>
78
  //           => (canon thread.suspend-then-resume cancel? (core func)) ๐Ÿงต
79
  //         | 0x2b cancel?:<cancel?>
80
  //           => (canon thread.yield-then-resume cancel? (core func)) ๐Ÿงต
81
  //         | 0x2c cancel?:<cancel?>
82
  //           => (canon thread.suspend-then-promote cancel? (core func)) ๐Ÿงต
83
  //         | 0x2d cancel?:<cancel?>
84
  //           => (canon thread.yield-then-promote cancel? (core func)) ๐Ÿงต
85
  //         | 0x40 shared?:<sh?> ft:<core:typeidx>
86
  //           => (canon thread.spawn-ref shared? ft (core func)) ๐Ÿงตโ‘ก
87
  //         | 0x41 shared?:<sh?> ft:<core:typeidx> tbl:<core:tableidx>
88
  //           => (canon thread.spawn-indirect shared? ft tbl (core func)) ๐Ÿงตโ‘ก
89
  //         | 0x42 shared?:<sh?>
90
  //           => (canon thread.available-parallelism shared? (core func)) ๐Ÿงตโ‘ก
91
  // async?  ::= 0x00 => ฯต
92
  //           | 0x01 => async
93
  // cancel? ::= 0x00 => ฯต
94
  //           | 0x01 => cancellable
95
  // sh?     ::= 0x00 => ฯต
96
  //           | 0x01 => shared ๐Ÿงตโ‘ก
97
98
  // Helper: load the async? / cancel? immediate. They share one encoding.
99
68.9k
  auto LoadFlagImm = [this, &ReportError, &C]() -> Expect<void> {
100
4.18k
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
101
4.15k
    if (B == 0x00) {
102
3.43k
      C.setFlagImmediate(false);
103
3.43k
    } else if (B == 0x01) {
104
710
      C.setFlagImmediate(true);
105
710
    } else {
106
17
      return ReportError(ErrCode::Value::MalformedCanonical);
107
17
    }
108
4.14k
    return {};
109
4.15k
  };
110
111
  // Helper: load the ๐Ÿงตโ‘ก shared? flag.
112
68.9k
  auto LoadShared = [this, &ReportError]() -> Expect<void> {
113
449
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
114
448
    if (B > 0x01) {
115
12
      return ReportError(ErrCode::Value::MalformedCanonical);
116
12
    }
117
436
    return {};
118
448
  };
119
120
  // Helper: load opts (vec of canonopt).
121
68.9k
  auto LoadOpts = [this, &C]() -> Expect<void> {
122
16.6k
    std::vector<AST::Component::CanonOpt> Opts;
123
16.6k
    EXPECTED_TRY(loadVec<AST::Component::Canonical>(
124
16.4k
        Opts, [this](AST::Component::CanonOpt &Opt) {
125
16.4k
          return loadCanonicalOption(Opt);
126
16.4k
        }));
127
16.4k
    C.setOptions(std::move(Opts));
128
16.4k
    return {};
129
16.6k
  };
130
131
68.9k
  EXPECTED_TRY(uint8_t Flag, FMgr.readByte().map_error(ReportError));
132
68.8k
  auto Code = static_cast<ComponentCanonOpCode>(Flag);
133
68.8k
  switch (Code) {
134
135
  // 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx>
136
10.8k
  case ComponentCanonOpCode::Lift: {
137
10.8k
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
138
10.8k
    if (unlikely(B != 0x00)) {
139
24
      return ReportError(ErrCode::Value::MalformedCanonical);
140
24
    }
141
21.6k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
142
21.6k
    C.setIndex(Idx);
143
21.6k
    EXPECTED_TRY(LoadOpts());
144
10.7k
    EXPECTED_TRY(uint32_t TypeIdx, FMgr.readU32().map_error(ReportError));
145
10.7k
    C.setTargetIndex(TypeIdx);
146
10.7k
    break;
147
10.7k
  }
148
149
  // 0x01 0x00 f:<funcidx> opts:<opts>
150
2.46k
  case ComponentCanonOpCode::Lower: {
151
2.46k
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
152
2.46k
    if (unlikely(B != 0x00)) {
153
10
      return ReportError(ErrCode::Value::MalformedCanonical);
154
10
    }
155
4.90k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
156
4.90k
    C.setIndex(Idx);
157
4.90k
    EXPECTED_TRY(LoadOpts());
158
2.43k
    break;
159
4.90k
  }
160
161
  // typeidx-only opcodes
162
2.43k
  case ComponentCanonOpCode::Resource__new:
163
739
  case ComponentCanonOpCode::Resource__drop:
164
1.05k
  case ComponentCanonOpCode::Resource__rep:
165
1.37k
  case ComponentCanonOpCode::Stream__new:
166
1.70k
  case ComponentCanonOpCode::Stream__drop_readable:
167
2.57k
  case ComponentCanonOpCode::Stream__drop_writable:
168
37.1k
  case ComponentCanonOpCode::Future__new:
169
37.5k
  case ComponentCanonOpCode::Future__drop_readable:
170
39.0k
  case ComponentCanonOpCode::Future__drop_writable: {
171
39.0k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
172
38.9k
    C.setIndex(Idx);
173
38.9k
    break;
174
39.0k
  }
175
176
  // no-arg opcodes
177
263
  case ComponentCanonOpCode::Backpressure__inc:
178
745
  case ComponentCanonOpCode::Backpressure__dec:
179
1.20k
  case ComponentCanonOpCode::Thread__index:
180
1.42k
  case ComponentCanonOpCode::Thread__resume_later:
181
1.79k
  case ComponentCanonOpCode::Task__cancel:
182
2.81k
  case ComponentCanonOpCode::Subtask__drop:
183
3.30k
  case ComponentCanonOpCode::Error_context__drop:
184
3.76k
  case ComponentCanonOpCode::Waitable_set__new:
185
4.07k
  case ComponentCanonOpCode::Waitable_set__drop:
186
4.69k
  case ComponentCanonOpCode::Waitable__join:
187
4.69k
    break;
188
189
  // 0x09 rs:<resultlist> opts:<opts>
190
1.76k
  case ComponentCanonOpCode::Task__return: {
191
    // Load resultlist (same encoding as functype resultlist).
192
1.76k
    EXPECTED_TRY(uint8_t RFlag, FMgr.readByte().map_error(ReportError));
193
1.75k
    switch (RFlag) {
194
1.31k
    case 0x00: {
195
1.31k
      ComponentValType VT;
196
1.31k
      EXPECTED_TRY(loadType(VT).map_error([](auto E) {
197
1.30k
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
198
1.30k
        return E;
199
1.30k
      }));
200
1.30k
      C.setResultList(VT);
201
1.30k
      break;
202
1.31k
    }
203
443
    case 0x01: {
204
443
      std::vector<AST::Component::LabelValType> ResultList;
205
443
      EXPECTED_TRY(loadVec<AST::Component::Canonical>(
206
394
          ResultList,
207
394
          [this](AST::Component::LabelValType &LV) { return loadType(LV); }));
208
394
      C.setResultList(std::move(ResultList));
209
394
      break;
210
443
    }
211
2
    default:
212
2
      return ReportError(ErrCode::Value::MalformedCanonical);
213
1.75k
    }
214
1.70k
    EXPECTED_TRY(LoadOpts());
215
1.67k
    break;
216
1.70k
  }
217
218
  // 0x0a t:<core:valtype> i:<u32> and 0x0b t:<core:valtype> i:<u32>
219
2.19k
  case ComponentCanonOpCode::Context__get:
220
3.15k
  case ComponentCanonOpCode::Context__set: {
221
3.15k
    EXPECTED_TRY(ValType T, loadValType(ASTNodeAttr::Comp_Canonical));
222
3.14k
    C.setContextType(T);
223
3.14k
    EXPECTED_TRY(uint32_t Val, FMgr.readU32().map_error(ReportError));
224
3.14k
    C.setConstVal(Val);
225
3.14k
    break;
226
3.14k
  }
227
228
  // async?/cancel?-only opcodes
229
175
  case ComponentCanonOpCode::Yield:
230
489
  case ComponentCanonOpCode::Subtask__cancel:
231
1.24k
  case ComponentCanonOpCode::Thread__suspend:
232
1.34k
  case ComponentCanonOpCode::Thread__suspend_then_resume:
233
1.89k
  case ComponentCanonOpCode::Thread__yield_then_resume:
234
2.26k
  case ComponentCanonOpCode::Thread__suspend_then_promote:
235
2.37k
  case ComponentCanonOpCode::Thread__yield_then_promote: {
236
2.37k
    EXPECTED_TRY(LoadFlagImm());
237
2.34k
    break;
238
2.37k
  }
239
240
  // typeidx + opts opcodes
241
2.34k
  case ComponentCanonOpCode::Stream__read:
242
1.04k
  case ComponentCanonOpCode::Stream__write:
243
1.36k
  case ComponentCanonOpCode::Future__read:
244
1.48k
  case ComponentCanonOpCode::Future__write: {
245
1.48k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
246
1.47k
    C.setIndex(Idx);
247
1.47k
    EXPECTED_TRY(LoadOpts());
248
1.44k
    break;
249
1.47k
  }
250
251
  // typeidx + async? opcodes
252
1.44k
  case ComponentCanonOpCode::Stream__cancel_read:
253
987
  case ComponentCanonOpCode::Stream__cancel_write:
254
1.21k
  case ComponentCanonOpCode::Future__cancel_read:
255
1.60k
  case ComponentCanonOpCode::Future__cancel_write: {
256
1.60k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
257
1.58k
    C.setIndex(Idx);
258
1.58k
    EXPECTED_TRY(LoadFlagImm());
259
1.58k
    break;
260
1.58k
  }
261
262
  // opts-only opcodes
263
1.58k
  case ComponentCanonOpCode::Error_context__new:
264
216
  case ComponentCanonOpCode::Error_context__debug_message: {
265
216
    EXPECTED_TRY(LoadOpts());
266
185
    break;
267
216
  }
268
269
  // async? + memidx opcodes
270
185
  case ComponentCanonOpCode::Waitable_set__wait:
271
224
  case ComponentCanonOpCode::Waitable_set__poll: {
272
224
    EXPECTED_TRY(LoadFlagImm());
273
213
    EXPECTED_TRY(uint32_t MemIdx, FMgr.readU32().map_error(ReportError));
274
208
    C.setIndex(MemIdx);
275
208
    break;
276
213
  }
277
278
  // 0x27 ft:<core:typeidx> tbl:<core:tableidx>
279
499
  case ComponentCanonOpCode::Thread__new_indirect: {
280
499
    EXPECTED_TRY(uint32_t TypeIdx, FMgr.readU32().map_error(ReportError));
281
497
    C.setIndex(TypeIdx);
282
497
    EXPECTED_TRY(uint32_t TblIdx, FMgr.readU32().map_error(ReportError));
283
495
    C.setTargetIndex(TblIdx);
284
495
    break;
285
497
  }
286
287
  // ๐Ÿงตโ‘ก shared?-prefixed opcodes. The flag is parsed but not kept, because
288
  // these built-ins belong to shared-everything-threads and are not
289
  // instantiated.
290
78
  case ComponentCanonOpCode::Thread__spawn_ref: {
291
78
    EXPECTED_TRY(LoadShared());
292
75
    EXPECTED_TRY(uint32_t TypeIdx, FMgr.readU32().map_error(ReportError));
293
72
    C.setIndex(TypeIdx);
294
72
    break;
295
75
  }
296
80
  case ComponentCanonOpCode::Thread__spawn_indirect: {
297
80
    EXPECTED_TRY(LoadShared());
298
152
    EXPECTED_TRY(uint32_t TypeIdx, FMgr.readU32().map_error(ReportError));
299
152
    C.setIndex(TypeIdx);
300
152
    EXPECTED_TRY(uint32_t TblIdx, FMgr.readU32().map_error(ReportError));
301
74
    C.setTargetIndex(TblIdx);
302
74
    break;
303
152
  }
304
291
  case ComponentCanonOpCode::Thread__available_parallelism:
305
291
    EXPECTED_TRY(LoadShared());
306
284
    break;
307
308
284
  default:
309
30
    return ReportError(ErrCode::Value::MalformedCanonical);
310
68.8k
  }
311
68.3k
  C.setOpCode(Code);
312
68.3k
  return {};
313
68.8k
}
314
315
7.64k
Expect<void> Loader::loadCanonicalOption(AST::Component::CanonOpt &Opt) {
316
7.64k
  auto ReportError = [this](auto E) {
317
100
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_CanonOpt);
318
100
  };
component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonicalOption(WasmEdge::AST::Component::CanonOpt&)::$_0::operator()<WasmEdge::ErrCode>(WasmEdge::ErrCode) const
Line
Count
Source
316
48
  auto ReportError = [this](auto E) {
317
48
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_CanonOpt);
318
48
  };
component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonicalOption(WasmEdge::AST::Component::CanonOpt&)::$_0::operator()<WasmEdge::ErrCode::Value>(WasmEdge::ErrCode::Value) const
Line
Count
Source
316
52
  auto ReportError = [this](auto E) {
317
52
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_CanonOpt);
318
52
  };
319
  // canonopt ::= 0x00                  => string-encoding=utf8
320
  //            | 0x01                  => string-encoding=utf16
321
  //            | 0x02                  => string-encoding=latin1+utf16
322
  //            | 0x03 m:<core:memidx>  => (memory m)
323
  //            | 0x04 f:<core:funcidx> => (realloc f)
324
  //            | 0x05 f:<core:funcidx> => (post-return f)
325
  //            | 0x06                  => async ๐Ÿ”€
326
  //            | 0x07 f:<core:funcidx> => (callback f) ๐Ÿ”€
327
328
7.64k
  EXPECTED_TRY(uint8_t Flag, FMgr.readByte().map_error(ReportError));
329
7.61k
  const auto Code = static_cast<ComponentCanonOptCode>(Flag);
330
7.61k
  switch (Code) {
331
3.91k
  case ComponentCanonOptCode::Encode_UTF8:
332
5.16k
  case ComponentCanonOptCode::Encode_UTF16:
333
5.80k
  case ComponentCanonOptCode::Encode_Latin1:
334
6.16k
  case ComponentCanonOptCode::Async:
335
6.16k
    break;
336
297
  case ComponentCanonOptCode::Memory:
337
609
  case ComponentCanonOptCode::Realloc:
338
1.15k
  case ComponentCanonOptCode::PostReturn:
339
1.40k
  case ComponentCanonOptCode::Callback: {
340
1.40k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
341
1.38k
    Opt.setIndex(Idx);
342
1.38k
    break;
343
1.40k
  }
344
52
  default:
345
52
    return ReportError(ErrCode::Value::UnknownCanonicalOption);
346
7.61k
  }
347
7.54k
  Opt.setCode(Code);
348
7.54k
  return {};
349
7.61k
}
350
351
} // namespace Loader
352
} // namespace WasmEdge