Coverage Report

Created: 2026-11-09 06:49

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
#include "common/component_valtype.h"
7
8
namespace WasmEdge {
9
namespace Loader {
10
11
301k
Expect<void> Loader::loadCanonical(AST::Component::Canonical &C) {
12
301k
  auto ReportError = [this](auto E) {
13
337
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_Canonical);
14
337
  };
component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonical(WasmEdge::AST::Component::Canonical&)::$_0::operator()<WasmEdge::ErrCode>(WasmEdge::ErrCode) const
Line
Count
Source
12
252
  auto ReportError = [this](auto E) {
13
252
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_Canonical);
14
252
  };
component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonical(WasmEdge::AST::Component::Canonical&)::$_0::operator()<WasmEdge::ErrCode::Value>(WasmEdge::ErrCode::Value) const
Line
Count
Source
12
85
  auto ReportError = [this](auto E) {
13
85
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_Canonical);
14
85
  };
15
  // canon ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx>
16
  //           => (canon lift f opts type-index-space[ft])
17
  //         | 0x01 0x00 f:<funcidx> opts:<opts>
18
  //           => (canon lower f opts (core func))
19
  //         | 0x02 rt:<typeidx>    => (canon resource.new rt (core func))
20
  //         | 0x03 rt:<typeidx>    => (canon resource.drop rt (core func))
21
  //         | 0x04 rt:<typeidx>    => (canon resource.rep rt (core func))
22
  //         | 0x09 rs:<resultlist> opts:<opts>
23
  //           => (canon task.return rs opts (core func)) ๐Ÿ”€
24
  //         | 0x05                 => (canon task.cancel (core func)) ๐Ÿ”€
25
  //         | 0x0a t:<core:valtype> i:<u32>
26
  //           => (canon context.get t i (core func)) ๐Ÿ”€
27
  //         | 0x0b t:<core:valtype> i:<u32>
28
  //           => (canon context.set t i (core func)) ๐Ÿ”€
29
  //         | 0x0c 0x00
30
  //           => (canon thread.yield (core func)) ๐Ÿ”€
31
  //         | 0x06 async?:<async?>
32
  //           => (canon subtask.cancel async? (core func)) ๐Ÿ”€
33
  //         | 0x0d                 => (canon subtask.drop (core func)) ๐Ÿ”€
34
  //         | 0x0e t:<typeidx>     => (canon stream.new t (core func)) ๐Ÿ”€
35
  //         | 0x0f t:<typeidx> opts:<opts>
36
  //           => (canon stream.read t opts (core func)) ๐Ÿ”€
37
  //         | 0x10 t:<typeidx> opts:<opts>
38
  //           => (canon stream.write t opts (core func)) ๐Ÿ”€
39
  //         | 0x11 t:<typeidx> async?:<async?>
40
  //           => (canon stream.cancel-read t async? (core func)) ๐Ÿ”€
41
  //         | 0x12 t:<typeidx> async?:<async?>
42
  //           => (canon stream.cancel-write t async? (core func)) ๐Ÿ”€
43
  //         | 0x13 t:<typeidx>
44
  //           => (canon stream.drop-readable t (core func)) ๐Ÿ”€
45
  //         | 0x14 t:<typeidx>
46
  //           => (canon stream.drop-writable t (core func)) ๐Ÿ”€
47
  //         | 0x2e t:<typeidx> => (canon stream.forward t (core func)) โžก๏ธ
48
  //         | 0x15 t:<typeidx> => (canon future.new t (core func)) ๐Ÿ”€
49
  //         | 0x16 t:<typeidx> opts:<opts>
50
  //           => (canon future.read t opts (core func)) ๐Ÿ”€
51
  //         | 0x17 t:<typeidx> opts:<opts>
52
  //           => (canon future.write t opts (core func)) ๐Ÿ”€
53
  //         | 0x18 t:<typeidx> async?:<async?>
54
  //           => (canon future.cancel-read t async? (core func)) ๐Ÿ”€
55
  //         | 0x19 t:<typeidx> async?:<async?>
56
  //           => (canon future.cancel-write t async? (core func)) ๐Ÿ”€
57
  //         | 0x1a t:<typeidx>
58
  //           => (canon future.drop-readable t (core func)) ๐Ÿ”€
59
  //         | 0x1b t:<typeidx>
60
  //           => (canon future.drop-writable t (core func)) ๐Ÿ”€
61
  //         | 0x2f t:<typeidx> => (canon future.forward t (core func)) โžก๏ธ
62
  //         | 0x1c opts:<opts> => (canon error-context.new opts (core func)) ๐Ÿ“
63
  //         | 0x1d opts:<opts>
64
  //           => (canon error-context.debug-message opts (core func)) ๐Ÿ“
65
  //         | 0x1e                => (canon error-context.drop (core func)) ๐Ÿ“
66
  //         | 0x1f                => (canon waitable-set.new (core func)) ๐Ÿ”€
67
  //         | 0x20 0x00 m:<core:memoryidx>
68
  //           => (canon waitable-set.wait (memory m) (core func)) ๐Ÿ”€
69
  //         | 0x21 0x00 m:<core:memoryidx>
70
  //           => (canon waitable-set.poll (memory m) (core func)) ๐Ÿ”€
71
  //         | 0x22                => (canon waitable-set.drop (core func)) ๐Ÿ”€
72
  //         | 0x23                => (canon waitable.join (core func)) ๐Ÿ”€
73
  //         | 0x24                => (canon backpressure.inc (core func)) ๐Ÿ”€
74
  //         | 0x25                => (canon backpressure.dec (core func)) ๐Ÿ”€
75
  //         | 0x26                => (canon thread.index (core func)) ๐Ÿงต
76
  //         | 0x27 ft:<core:typeidx> t:<core:tableidx>
77
  //           => (canon thread.new-indirect ft t (core func)) ๐Ÿงต
78
  //         | 0x28                => (canon thread.resume-later (core func)) ๐Ÿงต
79
  //         | 0x29 0x00
80
  //           => (canon thread.suspend (core func)) ๐Ÿงต
81
  //         | 0x2a 0x00
82
  //           => (canon thread.suspend-then-resume (core func)) ๐Ÿงต
83
  //         | 0x2b 0x00
84
  //           => (canon thread.yield-then-resume (core func)) ๐Ÿงต
85
  //         | 0x2c 0x00
86
  //           => (canon thread.suspend-then-promote (core func)) ๐Ÿงต
87
  //         | 0x2d 0x00
88
  //           => (canon thread.yield-then-promote (core func)) ๐Ÿงต
89
  //         | 0x40 sh?:<sh?> ft:<core:typeidx>
90
  //           => (canon thread.spawn-ref sh? ft (core func)) ๐Ÿงตโ‘ก
91
  //         | 0x41 sh?:<sh?> ft:<core:typeidx> t:<core:tableidx>
92
  //           => (canon thread.spawn-indirect sh? ft t (core func)) ๐Ÿงตโ‘ก
93
  //         | 0x42 sh?:<sh?>
94
  //           => (canon thread.available-parallelism sh? (core func)) ๐Ÿงตโ‘ก
95
  // async?  ::= 0x00 => ฯต
96
  //           | 0x01 => async
97
  // sh?     ::= 0x00 => ฯต
98
  //           | 0x01 => shared ๐Ÿงตโ‘ก
99
100
  // Helper: load the vestigial 0x00 of the thread.* and waitable-set.*
101
  // built-ins.
102
301k
  auto LoadReservedByte = [this, &ReportError]() -> Expect<void> {
103
37.6k
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
104
37.6k
    if (B != 0x00) {
105
10
      return ReportError(ErrCode::Value::NonZeroReservedByte);
106
10
    }
107
37.6k
    return {};
108
37.6k
  };
109
110
  // Helper: load the async? immediate.
111
301k
  auto LoadFlagImm = [this, &ReportError, &C]() -> Expect<void> {
112
4.88k
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
113
4.87k
    if (B == 0x00) {
114
2.83k
      C.setFlagImmediate(false);
115
2.83k
    } else if (B == 0x01) {
116
2.02k
      C.setFlagImmediate(true);
117
2.02k
    } else {
118
8
      return ReportError(ErrCode::Value::MalformedCanonical);
119
8
    }
120
4.86k
    return {};
121
4.87k
  };
122
123
  // Helper: load the ๐Ÿงตโ‘ก shared? flag.
124
301k
  auto LoadShared = [this, &ReportError]() -> Expect<void> {
125
588
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
126
585
    if (B > 0x01) {
127
6
      return ReportError(ErrCode::Value::MalformedCanonical);
128
6
    }
129
579
    return {};
130
585
  };
131
132
  // Helper: load opts (vec of canonopt).
133
301k
  auto LoadOpts = [this, &C]() -> Expect<void> {
134
20.3k
    std::vector<AST::Component::CanonOpt> Opts;
135
20.3k
    EXPECTED_TRY(loadVec<AST::Component::Canonical>(
136
20.1k
        Opts, [this](AST::Component::CanonOpt &Opt) {
137
20.1k
          return loadCanonicalOption(Opt);
138
20.1k
        }));
139
20.1k
    C.setOptions(std::move(Opts));
140
20.1k
    return {};
141
20.3k
  };
142
143
301k
  EXPECTED_TRY(uint8_t Flag, FMgr.readByte().map_error(ReportError));
144
301k
  auto Code = static_cast<ComponentCanonOpCode>(Flag);
145
301k
  switch (Code) {
146
147
  // 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx>
148
11.1k
  case ComponentCanonOpCode::Lift: {
149
11.1k
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
150
11.0k
    if (unlikely(B != 0x00)) {
151
27
      return ReportError(ErrCode::Value::MalformedCanonical);
152
27
    }
153
22.1k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
154
22.1k
    C.setIndex(Idx);
155
22.1k
    EXPECTED_TRY(LoadOpts());
156
10.9k
    EXPECTED_TRY(uint32_t TypeIdx, FMgr.readU32().map_error(ReportError));
157
10.9k
    C.setTargetIndex(TypeIdx);
158
10.9k
    break;
159
10.9k
  }
160
161
  // 0x01 0x00 f:<funcidx> opts:<opts>
162
3.11k
  case ComponentCanonOpCode::Lower: {
163
3.11k
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
164
3.11k
    if (unlikely(B != 0x00)) {
165
3
      return ReportError(ErrCode::Value::MalformedCanonical);
166
3
    }
167
6.21k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
168
6.21k
    C.setIndex(Idx);
169
6.21k
    EXPECTED_TRY(LoadOpts());
170
3.09k
    break;
171
6.21k
  }
172
173
  // typeidx-only opcodes
174
3.09k
  case ComponentCanonOpCode::Resource__new:
175
1.44k
  case ComponentCanonOpCode::Resource__drop:
176
2.24k
  case ComponentCanonOpCode::Resource__rep:
177
3.58k
  case ComponentCanonOpCode::Stream__new:
178
3.85k
  case ComponentCanonOpCode::Stream__drop_readable:
179
4.11k
  case ComponentCanonOpCode::Stream__drop_writable:
180
4.39k
  case ComponentCanonOpCode::Stream__forward:
181
78.2k
  case ComponentCanonOpCode::Future__new:
182
78.6k
  case ComponentCanonOpCode::Future__drop_readable:
183
213k
  case ComponentCanonOpCode::Future__drop_writable:
184
214k
  case ComponentCanonOpCode::Future__forward: {
185
214k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
186
214k
    C.setIndex(Idx);
187
214k
    break;
188
214k
  }
189
190
  // no-arg opcodes
191
408
  case ComponentCanonOpCode::Backpressure__inc:
192
839
  case ComponentCanonOpCode::Backpressure__dec:
193
2.06k
  case ComponentCanonOpCode::Thread__index:
194
2.39k
  case ComponentCanonOpCode::Thread__resume_later:
195
2.77k
  case ComponentCanonOpCode::Task__cancel:
196
10.4k
  case ComponentCanonOpCode::Subtask__drop:
197
11.7k
  case ComponentCanonOpCode::Error_context__drop:
198
16.6k
  case ComponentCanonOpCode::Waitable_set__new:
199
17.0k
  case ComponentCanonOpCode::Waitable_set__drop:
200
18.2k
  case ComponentCanonOpCode::Waitable__join:
201
18.2k
    break;
202
203
  // 0x09 rs:<resultlist> opts:<opts>
204
3.87k
  case ComponentCanonOpCode::Task__return: {
205
    // resultlist ::= 0x00 t:<valtype> => (result t)
206
    //              | 0x01 0x00        => ฯต
207
3.87k
    EXPECTED_TRY(uint8_t RFlag, FMgr.readByte().map_error(ReportError));
208
3.87k
    switch (RFlag) {
209
3.43k
    case 0x00: {
210
3.43k
      ComponentValType VT;
211
3.43k
      EXPECTED_TRY(loadType(VT).map_error([](auto E) {
212
3.42k
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
213
3.42k
        return E;
214
3.42k
      }));
215
3.42k
      C.setResult(VT);
216
3.42k
      break;
217
3.43k
    }
218
440
    case 0x01: {
219
440
      EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
220
439
      if (B != 0x00) {
221
1
        return ReportError(ErrCode::Value::MalformedCanonical);
222
1
      }
223
438
      C.setResult(std::nullopt);
224
438
      break;
225
439
    }
226
1
    default:
227
1
      return ReportError(ErrCode::Value::MalformedCanonical);
228
3.87k
    }
229
3.86k
    EXPECTED_TRY(LoadOpts());
230
3.84k
    break;
231
3.86k
  }
232
233
  // 0x0a t:<core:valtype> i:<u32> and 0x0b t:<core:valtype> i:<u32>
234
4.51k
  case ComponentCanonOpCode::Context__get:
235
4.87k
  case ComponentCanonOpCode::Context__set: {
236
4.87k
    EXPECTED_TRY(ValType T, loadValType(ASTNodeAttr::Comp_Canonical));
237
4.86k
    C.setContextType(T);
238
4.86k
    EXPECTED_TRY(uint32_t Val, FMgr.readU32().map_error(ReportError));
239
4.86k
    C.setConstVal(Val);
240
4.86k
    break;
241
4.86k
  }
242
243
  // 0x06 async?:<async?>
244
365
  case ComponentCanonOpCode::Subtask__cancel: {
245
365
    EXPECTED_TRY(LoadFlagImm());
246
359
    break;
247
365
  }
248
249
  // 0x00-suffixed opcodes
250
359
  case ComponentCanonOpCode::Thread__yield:
251
428
  case ComponentCanonOpCode::Thread__suspend:
252
1.20k
  case ComponentCanonOpCode::Thread__suspend_then_resume:
253
1.53k
  case ComponentCanonOpCode::Thread__yield_then_resume:
254
37.0k
  case ComponentCanonOpCode::Thread__suspend_then_promote:
255
37.3k
  case ComponentCanonOpCode::Thread__yield_then_promote: {
256
37.3k
    EXPECTED_TRY(LoadReservedByte());
257
37.3k
    break;
258
37.3k
  }
259
260
  // typeidx + opts opcodes
261
37.3k
  case ComponentCanonOpCode::Stream__read:
262
1.41k
  case ComponentCanonOpCode::Stream__write:
263
1.86k
  case ComponentCanonOpCode::Future__read:
264
2.02k
  case ComponentCanonOpCode::Future__write: {
265
2.02k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
266
2.00k
    C.setIndex(Idx);
267
2.00k
    EXPECTED_TRY(LoadOpts());
268
1.98k
    break;
269
2.00k
  }
270
271
  // typeidx + async? opcodes
272
1.98k
  case ComponentCanonOpCode::Stream__cancel_read:
273
960
  case ComponentCanonOpCode::Stream__cancel_write:
274
1.43k
  case ComponentCanonOpCode::Future__cancel_read:
275
4.53k
  case ComponentCanonOpCode::Future__cancel_write: {
276
4.53k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
277
4.51k
    C.setIndex(Idx);
278
4.51k
    EXPECTED_TRY(LoadFlagImm());
279
4.50k
    break;
280
4.51k
  }
281
282
  // opts-only opcodes
283
4.50k
  case ComponentCanonOpCode::Error_context__new:
284
281
  case ComponentCanonOpCode::Error_context__debug_message: {
285
281
    EXPECTED_TRY(LoadOpts());
286
234
    break;
287
281
  }
288
289
  // 0x00 + memidx opcodes
290
234
  case ComponentCanonOpCode::Waitable_set__wait:
291
275
  case ComponentCanonOpCode::Waitable_set__poll: {
292
275
    EXPECTED_TRY(LoadReservedByte());
293
265
    EXPECTED_TRY(uint32_t MemIdx, FMgr.readU32().map_error(ReportError));
294
263
    C.setIndex(MemIdx);
295
263
    break;
296
265
  }
297
298
  // 0x27 ft:<core:typeidx> t:<core:tableidx>
299
220
  case ComponentCanonOpCode::Thread__new_indirect: {
300
220
    EXPECTED_TRY(uint32_t TypeIdx, FMgr.readU32().map_error(ReportError));
301
219
    C.setIndex(TypeIdx);
302
219
    EXPECTED_TRY(uint32_t TblIdx, FMgr.readU32().map_error(ReportError));
303
215
    C.setTargetIndex(TblIdx);
304
215
    break;
305
219
  }
306
307
  // ๐Ÿงตโ‘ก shared?-prefixed opcodes. The flag is parsed but not kept, because
308
  // these built-ins belong to shared-everything-threads and are not
309
  // instantiated.
310
83
  case ComponentCanonOpCode::Thread__spawn_ref: {
311
83
    EXPECTED_TRY(LoadShared());
312
80
    EXPECTED_TRY(uint32_t TypeIdx, FMgr.readU32().map_error(ReportError));
313
79
    C.setIndex(TypeIdx);
314
79
    break;
315
80
  }
316
142
  case ComponentCanonOpCode::Thread__spawn_indirect: {
317
142
    EXPECTED_TRY(LoadShared());
318
279
    EXPECTED_TRY(uint32_t TypeIdx, FMgr.readU32().map_error(ReportError));
319
279
    C.setIndex(TypeIdx);
320
279
    EXPECTED_TRY(uint32_t TblIdx, FMgr.readU32().map_error(ReportError));
321
138
    C.setTargetIndex(TblIdx);
322
138
    break;
323
279
  }
324
363
  case ComponentCanonOpCode::Thread__available_parallelism:
325
363
    EXPECTED_TRY(LoadShared());
326
359
    break;
327
328
359
  default:
329
29
    return ReportError(ErrCode::Value::MalformedCanonical);
330
301k
  }
331
300k
  C.setOpCode(Code);
332
300k
  return {};
333
301k
}
334
335
11.8k
Expect<void> Loader::loadCanonicalOption(AST::Component::CanonOpt &Opt) {
336
11.8k
  auto ReportError = [this](auto E) {
337
112
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_CanonOpt);
338
112
  };
component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonicalOption(WasmEdge::AST::Component::CanonOpt&)::$_0::operator()<WasmEdge::ErrCode>(WasmEdge::ErrCode) const
Line
Count
Source
336
48
  auto ReportError = [this](auto E) {
337
48
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_CanonOpt);
338
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
336
64
  auto ReportError = [this](auto E) {
337
64
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_CanonOpt);
338
64
  };
339
  // canonopt ::= 0x00                  => string-encoding=utf8
340
  //            | 0x01                  => string-encoding=utf16
341
  //            | 0x02                  => string-encoding=latin1+utf16
342
  //            | 0x03 m:<core:memidx>  => (memory m)
343
  //            | 0x04 f:<core:funcidx> => (realloc f)
344
  //            | 0x05 f:<core:funcidx> => (post-return f)
345
  //            | 0x06                  => async ๐Ÿ”€
346
  //            | 0x07 f:<core:funcidx> => (callback f) ๐Ÿ”€
347
348
11.8k
  EXPECTED_TRY(uint8_t Flag, FMgr.readByte().map_error(ReportError));
349
11.8k
  const auto Code = static_cast<ComponentCanonOptCode>(Flag);
350
11.8k
  switch (Code) {
351
5.72k
  case ComponentCanonOptCode::Encode_UTF8:
352
8.08k
  case ComponentCanonOptCode::Encode_UTF16:
353
8.57k
  case ComponentCanonOptCode::Encode_Latin1:
354
9.48k
  case ComponentCanonOptCode::Async:
355
9.48k
    break;
356
400
  case ComponentCanonOptCode::Memory:
357
628
  case ComponentCanonOptCode::Realloc:
358
2.06k
  case ComponentCanonOptCode::PostReturn:
359
2.28k
  case ComponentCanonOptCode::Callback: {
360
2.28k
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
361
2.26k
    Opt.setIndex(Idx);
362
2.26k
    break;
363
2.28k
  }
364
64
  default:
365
64
    return ReportError(ErrCode::Value::UnknownCanonicalOption);
366
11.8k
  }
367
11.7k
  Opt.setCode(Code);
368
11.7k
  return {};
369
11.8k
}
370
371
} // namespace Loader
372
} // namespace WasmEdge