Coverage Report

Created: 2026-06-30 06:10

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
0
Expect<void> Loader::loadCanonical(AST::Component::Canonical &C) {
10
0
  auto ReportError = [this](auto E) {
11
0
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_Canonical);
12
0
  };
Unexecuted instantiation: component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonical(WasmEdge::AST::Component::Canonical&)::$_0::operator()<WasmEdge::ErrCode>(WasmEdge::ErrCode) const
Unexecuted instantiation: component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonical(WasmEdge::AST::Component::Canonical&)::$_0::operator()<WasmEdge::ErrCode::Value>(WasmEdge::ErrCode::Value) const
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
  //         | 0x07 rt:<typeidx>
20
  //           => (canon resource.drop rt async (core func)) ๐Ÿ”€
21
  //         | 0x04 rt:<typeidx>    => (canon resource.rep rt (core func))
22
  //         | 0x08                 => (canon backpressure.set (core func)) ๐Ÿ”€
23
  //         | 0x09 rs:<resultlist> opts:<opts>
24
  //           => (canon task.return rs opts (core func)) ๐Ÿ”€
25
  //         | 0x05                 => (canon task.cancel (core func)) ๐Ÿ”€
26
  //         | 0x0a 0x7f i:<u32>    => (canon context.get i32 i (core func)) ๐Ÿ”€
27
  //         | 0x0b 0x7f i:<u32>    => (canon context.set i32 i (core func)) ๐Ÿ”€
28
  //         | 0x0c async?:<async>? => (canon yield async? (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 async? (core func)) ๐Ÿ”€
39
  //         | 0x12 t:<typeidx> async?:<async?>
40
  //           => (canon stream.cancel-write async? (core func)) ๐Ÿ”€
41
  //         | 0x13 t:<typeidx>
42
  //           => (canon stream.close-readable t (core func)) ๐Ÿ”€
43
  //         | 0x14 t:<typeidx>
44
  //           => (canon stream.close-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 async? (core func)) ๐Ÿ”€
52
  //         | 0x19 t:<typeidx> async?:<async?>
53
  //           => (canon future.cancel-write async? (core func)) ๐Ÿ”€
54
  //         | 0x1a t:<typeidx>
55
  //           => (canon future.close-readable t (core func)) ๐Ÿ”€
56
  //         | 0x1b t:<typeidx>
57
  //           => (canon future.close-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 async?:<async>? m:<core:memidx>
64
  //           => (canon waitable-set.wait async? (memory m) (core func)) ๐Ÿ”€
65
  //         | 0x21 async?:<async>? m:<core:memidx>
66
  //           => (canon waitable-set.poll async? (memory m) (core func)) ๐Ÿ”€
67
  //         | 0x22                => (canon waitable-set.drop (core func)) ๐Ÿ”€
68
  //         | 0x23                => (canon waitable.join (core func)) ๐Ÿ”€
69
  //         | 0x40 ft:<typeidx>   => (canon thread.spawn_ref ft (core func)) ๐Ÿงต
70
  //         | 0x41 ft:<typeidx> tbl:<core:tableidx>
71
  //           => (canon thread.spawn_indirect ft tbl (core func)) ๐Ÿงต
72
  //         | 0x42 => (canon thread.available_parallelism (core func)) ๐Ÿงต
73
  // async? ::= 0x00 => ฯต
74
  //          | 0x01 => async
75
76
  // Helper: load async? flag.
77
0
  auto LoadAsync = [this, &ReportError, &C]() -> Expect<void> {
78
0
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
79
0
    if (B == 0x00) {
80
0
      C.setAsync(false);
81
0
    } else if (B == 0x01) {
82
0
      C.setAsync(true);
83
0
    } else {
84
0
      return ReportError(ErrCode::Value::MalformedCanonical);
85
0
    }
86
0
    return {};
87
0
  };
88
89
  // Helper: load opts (vec of canonopt).
90
0
  auto LoadOpts = [this, &C]() -> Expect<void> {
91
0
    std::vector<AST::Component::CanonOpt> Opts;
92
0
    EXPECTED_TRY(loadVec<AST::Component::Canonical>(
93
0
        Opts, [this](AST::Component::CanonOpt &Opt) {
94
0
          return loadCanonicalOption(Opt);
95
0
        }));
96
0
    C.setOptions(std::move(Opts));
97
0
    return {};
98
0
  };
99
100
0
  EXPECTED_TRY(uint8_t Flag, FMgr.readByte().map_error(ReportError));
101
0
  auto Code = static_cast<ComponentCanonOpCode>(Flag);
102
0
  switch (Code) {
103
104
  // 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx>
105
0
  case ComponentCanonOpCode::Lift: {
106
0
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
107
0
    if (unlikely(B != 0x00)) {
108
0
      return ReportError(ErrCode::Value::MalformedCanonical);
109
0
    }
110
0
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
111
0
    C.setIndex(Idx);
112
0
    EXPECTED_TRY(LoadOpts());
113
0
    EXPECTED_TRY(uint32_t TypeIdx, FMgr.readU32().map_error(ReportError));
114
0
    C.setTargetIndex(TypeIdx);
115
0
    break;
116
0
  }
117
118
  // 0x01 0x00 f:<funcidx> opts:<opts>
119
0
  case ComponentCanonOpCode::Lower: {
120
0
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
121
0
    if (unlikely(B != 0x00)) {
122
0
      return ReportError(ErrCode::Value::MalformedCanonical);
123
0
    }
124
0
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
125
0
    C.setIndex(Idx);
126
0
    EXPECTED_TRY(LoadOpts());
127
0
    break;
128
0
  }
129
130
  // typeidx-only opcodes
131
0
  case ComponentCanonOpCode::Resource__new:
132
0
  case ComponentCanonOpCode::Resource__drop:
133
0
  case ComponentCanonOpCode::Resource__drop_async:
134
0
  case ComponentCanonOpCode::Resource__rep:
135
0
  case ComponentCanonOpCode::Stream__new:
136
0
  case ComponentCanonOpCode::Stream__close_readable:
137
0
  case ComponentCanonOpCode::Stream__close_writable:
138
0
  case ComponentCanonOpCode::Future__new:
139
0
  case ComponentCanonOpCode::Future__close_readable:
140
0
  case ComponentCanonOpCode::Future__close_writable:
141
0
  case ComponentCanonOpCode::Thread__spawn_ref: {
142
0
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
143
0
    C.setIndex(Idx);
144
0
    break;
145
0
  }
146
147
  // no-arg opcodes
148
0
  case ComponentCanonOpCode::Backpressure__set:
149
0
  case ComponentCanonOpCode::Task__cancel:
150
0
  case ComponentCanonOpCode::Subtask__drop:
151
0
  case ComponentCanonOpCode::Error_context__drop:
152
0
  case ComponentCanonOpCode::Waitable_set__new:
153
0
  case ComponentCanonOpCode::Waitable_set__drop:
154
0
  case ComponentCanonOpCode::Waitable__join:
155
0
  case ComponentCanonOpCode::Thread__available_parallelism:
156
0
    break;
157
158
  // 0x09 rs:<resultlist> opts:<opts>
159
0
  case ComponentCanonOpCode::Task__return: {
160
    // Load resultlist (same encoding as functype resultlist).
161
0
    EXPECTED_TRY(uint8_t RFlag, FMgr.readByte().map_error(ReportError));
162
0
    switch (RFlag) {
163
0
    case 0x00: {
164
0
      ComponentValType VT;
165
0
      EXPECTED_TRY(loadType(VT).map_error([](auto E) {
166
0
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
167
0
        return E;
168
0
      }));
169
0
      C.setResultList(VT);
170
0
      break;
171
0
    }
172
0
    case 0x01: {
173
0
      std::vector<AST::Component::LabelValType> ResultList;
174
0
      EXPECTED_TRY(loadVec<AST::Component::Canonical>(
175
0
          ResultList,
176
0
          [this](AST::Component::LabelValType &LV) { return loadType(LV); }));
177
0
      C.setResultList(std::move(ResultList));
178
0
      break;
179
0
    }
180
0
    default:
181
0
      return ReportError(ErrCode::Value::MalformedCanonical);
182
0
    }
183
0
    EXPECTED_TRY(LoadOpts());
184
0
    break;
185
0
  }
186
187
  // 0x0a 0x7f i:<u32> and 0x0b 0x7f i:<u32>
188
0
  case ComponentCanonOpCode::Context__get:
189
0
  case ComponentCanonOpCode::Context__set: {
190
0
    EXPECTED_TRY(uint8_t B, FMgr.readByte().map_error(ReportError));
191
0
    if (unlikely(B != 0x7f)) {
192
0
      return ReportError(ErrCode::Value::MalformedCanonical);
193
0
    }
194
0
    EXPECTED_TRY(uint32_t Val, FMgr.readU32().map_error(ReportError));
195
0
    C.setConstVal(Val);
196
0
    break;
197
0
  }
198
199
  // async?-only opcodes
200
0
  case ComponentCanonOpCode::Yield:
201
0
  case ComponentCanonOpCode::Subtask__cancel: {
202
0
    EXPECTED_TRY(LoadAsync());
203
0
    break;
204
0
  }
205
206
  // typeidx + opts opcodes
207
0
  case ComponentCanonOpCode::Stream__read:
208
0
  case ComponentCanonOpCode::Stream__write:
209
0
  case ComponentCanonOpCode::Future__read:
210
0
  case ComponentCanonOpCode::Future__write: {
211
0
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
212
0
    C.setIndex(Idx);
213
0
    EXPECTED_TRY(LoadOpts());
214
0
    break;
215
0
  }
216
217
  // typeidx + async? opcodes
218
0
  case ComponentCanonOpCode::Stream__cancel_read:
219
0
  case ComponentCanonOpCode::Stream__cancel_write:
220
0
  case ComponentCanonOpCode::Future__cancel_read:
221
0
  case ComponentCanonOpCode::Future__cancel_write: {
222
0
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
223
0
    C.setIndex(Idx);
224
0
    EXPECTED_TRY(LoadAsync());
225
0
    break;
226
0
  }
227
228
  // opts-only opcodes
229
0
  case ComponentCanonOpCode::Error_context__new:
230
0
  case ComponentCanonOpCode::Error_context__debug_message: {
231
0
    EXPECTED_TRY(LoadOpts());
232
0
    break;
233
0
  }
234
235
  // async? + memidx opcodes
236
0
  case ComponentCanonOpCode::Waitable_set__wait:
237
0
  case ComponentCanonOpCode::Waitable_set__poll: {
238
0
    EXPECTED_TRY(LoadAsync());
239
0
    EXPECTED_TRY(uint32_t MemIdx, FMgr.readU32().map_error(ReportError));
240
0
    C.setIndex(MemIdx);
241
0
    break;
242
0
  }
243
244
  // 0x41 ft:<typeidx> tbl:<core:tableidx>
245
0
  case ComponentCanonOpCode::Thread__spawn_indirect: {
246
0
    EXPECTED_TRY(uint32_t TypeIdx, FMgr.readU32().map_error(ReportError));
247
0
    C.setIndex(TypeIdx);
248
0
    EXPECTED_TRY(uint32_t TblIdx, FMgr.readU32().map_error(ReportError));
249
0
    C.setTargetIndex(TblIdx);
250
0
    break;
251
0
  }
252
253
0
  default:
254
0
    return ReportError(ErrCode::Value::MalformedCanonical);
255
0
  }
256
0
  C.setOpCode(Code);
257
0
  return {};
258
0
}
259
260
0
Expect<void> Loader::loadCanonicalOption(AST::Component::CanonOpt &Opt) {
261
0
  auto ReportError = [this](auto E) {
262
0
    return logLoadError(E, FMgr.getLastOffset(), ASTNodeAttr::Comp_CanonOpt);
263
0
  };
Unexecuted instantiation: component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonicalOption(WasmEdge::AST::Component::CanonOpt&)::$_0::operator()<WasmEdge::ErrCode>(WasmEdge::ErrCode) const
Unexecuted instantiation: component_canonical.cpp:auto WasmEdge::Loader::Loader::loadCanonicalOption(WasmEdge::AST::Component::CanonOpt&)::$_0::operator()<WasmEdge::ErrCode::Value>(WasmEdge::ErrCode::Value) const
264
  // canonopt ::= 0x00                  => string-encoding=utf8
265
  //            | 0x01                  => string-encoding=utf16
266
  //            | 0x02                  => string-encoding=latin1+utf16
267
  //            | 0x03 m:<core:memidx>  => (memory m)
268
  //            | 0x04 f:<core:funcidx> => (realloc f)
269
  //            | 0x05 f:<core:funcidx> => (post-return f)
270
  //            | 0x06                  => async ๐Ÿ”€
271
  //            | 0x07 f:<core:funcidx> => (callback f) ๐Ÿ”€
272
  //            | 0x08                  => always-task-return ๐Ÿ”€
273
274
0
  EXPECTED_TRY(uint8_t Flag, FMgr.readByte().map_error(ReportError));
275
0
  switch (Flag) {
276
0
  case 0x00:
277
0
  case 0x01:
278
0
  case 0x02:
279
0
  case 0x06:
280
0
  case 0x08:
281
0
    break;
282
0
  case 0x03:
283
0
  case 0x04:
284
0
  case 0x05:
285
0
  case 0x07: {
286
0
    EXPECTED_TRY(uint32_t Idx, FMgr.readU32().map_error(ReportError));
287
0
    Opt.setIndex(Idx);
288
0
    break;
289
0
  }
290
0
  default:
291
0
    return ReportError(ErrCode::Value::UnknownCanonicalOption);
292
0
  }
293
0
  Opt.setCode(static_cast<ComponentCanonOptCode>(Flag));
294
0
  return {};
295
0
}
296
297
} // namespace Loader
298
} // namespace WasmEdge