Coverage Report

Created: 2026-09-28 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/validator/component_canon.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
//===-- component_canon.cpp - Canonical built-in validation ---------------===//
5
//
6
// Validation of canon lift / lower / resource.* definitions.
7
//
8
//===----------------------------------------------------------------------===//
9
10
#include "common/errinfo.h"
11
#include "common/spdlog.h"
12
#include "validator/validator.h"
13
14
#include <vector>
15
16
namespace WasmEdge {
17
namespace Validator {
18
19
using namespace std::literals;
20
21
// canon ::= lift | lower | resource.* | async built-in, one switch per opcode.
22
Expect<void>
23
45.8k
Validator::validate(const AST::Component::Canonical &Canon) noexcept {
24
45.8k
  auto &S = CompCtx.top();
25
45.8k
  const ValType I32V{TypeCode::I32};
26
45.8k
  const ValType I64V{TypeCode::I64};
27
28
  // Defines the core function a canonical definition lowers to.
29
45.8k
  auto PushCoreFunc = [&](std::vector<ValType> Params,
30
45.8k
                          std::vector<ValType> Results) -> Expect<void> {
31
39.8k
    S.addCoreFunc(CompTypes.addCoreFuncType(Params, Results));
32
39.8k
    return {};
33
39.8k
  };
34
  // A built-in takes its core type from the shared table, keyed on Addr.
35
45.8k
  auto PushBuiltin = [&](const ValType &Addr) -> Expect<void> {
36
34.5k
    auto [Params, Results] = AST::Component::Canonical::getBuiltinCoreFuncType(
37
34.5k
        Canon.getOpCode(), Addr);
38
34.5k
    return PushCoreFunc(std::move(Params), std::move(Results));
39
34.5k
  };
40
41
  // The resource built-ins take no options and name a resource type.
42
45.8k
  auto GetResourceEntry =
43
45.8k
      [&](std::string_view What) -> Expect<const Component::TypeEntry *> {
44
150
    if (!Canon.getOptions().empty()) {
45
0
      spdlog::error(ErrCode::Value::CanonOptionOnBuiltin);
46
0
      spdlog::error("    resource built-ins take no canonical options."sv);
47
0
      return Unexpect(ErrCode::Value::CanonOptionOnBuiltin);
48
0
    }
49
150
    const auto *Entry = S.getType(Canon.getIndex());
50
150
    if (Entry == nullptr) {
51
34
      spdlog::error(ErrCode::Value::ComponentTypeIndexOutOfBounds);
52
34
      spdlog::error("    {} type index {} out of bounds."sv, What,
53
34
                    Canon.getIndex());
54
34
      return Unexpect(ErrCode::Value::ComponentTypeIndexOutOfBounds);
55
34
    }
56
116
    if (!Entry->isResource()) {
57
8
      spdlog::error(ErrCode::Value::ComponentNotResourceType);
58
8
      spdlog::error("    {} type index {} is not a resource."sv, What,
59
8
                    Canon.getIndex());
60
8
      return Unexpect(ErrCode::Value::ComponentNotResourceType);
61
8
    }
62
108
    return Entry;
63
116
  };
64
65
  // resource.new / resource.rep need a locally-defined resource and its rep.
66
45.8k
  auto LocalResourceRep = [&](std::string_view What) -> Expect<ValType> {
67
71
    EXPECTED_TRY(const auto *Entry, GetResourceEntry(What));
68
38
    const auto &Res = CompTypes.getResource(*Entry->ResourceId);
69
38
    if (Res.RT == nullptr || Res.Origin != &S) {
70
1
      spdlog::error(ErrCode::Value::ComponentNotLocalResource);
71
1
      spdlog::error("    {} requires a locally-defined resource type."sv, What);
72
1
      return Unexpect(ErrCode::Value::ComponentNotLocalResource);
73
1
    }
74
37
    return Res.RT->isAddrI64() ? I64V : I32V;
75
38
  };
76
77
  // stream/future built-ins name their element type in the type immediate.
78
45.8k
  auto CheckTypeImmediate =
79
45.8k
      [&](bool WantStream) -> Expect<const Component::TypeEntry *> {
80
30.6k
    const auto *Entry = S.getType(Canon.getIndex());
81
30.6k
    const auto *Def = Entry != nullptr ? Entry->getDefValType() : nullptr;
82
30.6k
    const bool Ok =
83
30.6k
        Def != nullptr && (WantStream ? Def->isStreamTy() : Def->isFutureTy());
84
30.6k
    if (!Ok) {
85
210
      spdlog::error(ErrCode::Value::InvalidTypeReference);
86
210
      spdlog::error("    Built-in type index {} does not refer to a {} "
87
210
                    "type."sv,
88
210
                    Canon.getIndex(), WantStream ? "stream" : "future");
89
210
      return Unexpect(ErrCode::Value::InvalidTypeReference);
90
210
    }
91
30.4k
    return Entry;
92
30.6k
  };
93
94
  // Option-bearing built-ins are lowerings: no post-return or callback.
95
45.8k
  auto BuiltinOptions = [&](bool NeedMemory, bool NeedRealloc) -> Expect<void> {
96
834
    EXPECTED_TRY(CompCtx.checkOptions(Canon, false));
97
828
    return CompCtx.requireOptions(Canon, NeedMemory, NeedRealloc,
98
828
                                  "this canonical built-in"sv);
99
834
  };
100
101
  // Built-ins the spec restricts to synchronous use.
102
45.8k
  auto RequireSync = [&]() -> Expect<void> {
103
1.10k
    if (Canon.hasOption(ComponentCanonOptCode::Async)) {
104
2
      spdlog::error(ErrCode::Value::CanonAsyncOnBuiltin);
105
2
      spdlog::error("    this canonical built-in is always synchronous."sv);
106
2
      return Unexpect(ErrCode::Value::CanonAsyncOnBuiltin);
107
2
    }
108
1.10k
    return {};
109
1.10k
  };
110
111
45.8k
  bool ParamsNeedMemory = false, ResultsNeedMemory = false;
112
113
45.8k
  switch (Canon.getOpCode()) {
114
5.50k
  case ComponentCanonOpCode::Lift: {
115
5.50k
    const auto *Entry = S.getType(Canon.getTargetIndex());
116
5.50k
    if (Entry == nullptr) {
117
25
      spdlog::error(ErrCode::Value::ComponentTypeIndexOutOfBounds);
118
25
      spdlog::error("    canon lift type index {} out of bounds."sv,
119
25
                    Canon.getTargetIndex());
120
25
      return Unexpect(ErrCode::Value::ComponentTypeIndexOutOfBounds);
121
25
    }
122
5.48k
    if (Entry->getFuncType() == nullptr) {
123
3
      spdlog::error(ErrCode::Value::ComponentNotFunctionType);
124
3
      spdlog::error("    canon lift type index {} is not a function type."sv,
125
3
                    Canon.getTargetIndex());
126
3
      return Unexpect(ErrCode::Value::ComponentNotFunctionType);
127
3
    }
128
5.47k
    const Component::FuncInfo FI{Entry->getFuncType(), Entry->Home,
129
5.47k
                                 Entry->Remap};
130
5.47k
    EXPECTED_TRY(CompCtx.checkOptions(Canon, true));
131
5.42k
    const bool AsyncOpt = Canon.hasOption(ComponentCanonOptCode::Async);
132
5.42k
    if (AsyncOpt && !FI.FT->isAsync()) {
133
1
      spdlog::error(ErrCode::Value::CanonAsyncRequiresAsyncType);
134
1
      spdlog::error("    canon lift with `async` needs `(func async ...)`."sv);
135
1
      return Unexpect(ErrCode::Value::CanonAsyncRequiresAsyncType);
136
1
    }
137
5.42k
    if (Canon.hasOption(ComponentCanonOptCode::Callback) && !AsyncOpt) {
138
0
      spdlog::error(ErrCode::Value::CanonCallbackRequiresAsync);
139
0
      spdlog::error("    the `callback` option requires the `async` option."sv);
140
0
      return Unexpect(ErrCode::Value::CanonCallbackRequiresAsync);
141
0
    }
142
5.42k
    if (AsyncOpt && Canon.hasOption(ComponentCanonOptCode::PostReturn)) {
143
2
      spdlog::error(ErrCode::Value::CanonPostReturnWithAsync);
144
2
      spdlog::error("    cannot specify post-return function in combination "
145
2
                    "with async."sv);
146
2
      return Unexpect(ErrCode::Value::CanonPostReturnWithAsync);
147
2
    }
148
149
5.42k
    const ValType Ptr = CompCtx.getCanonPtrType(Canon);
150
5.42k
    EXPECTED_TRY(auto Sig, CompTypes.flattenFuncType(FI, Ptr, ParamsNeedMemory,
151
5.42k
                                                     ResultsNeedMemory));
152
5.42k
    const bool ParamsIndirect =
153
5.42k
        Sig.getParamTypes().size() > Component::TypeSystem::MaxFlatParams;
154
5.42k
    const bool ResultsIndirect =
155
5.42k
        Sig.getReturnTypes().size() > Component::TypeSystem::MaxFlatResults;
156
5.42k
    if (ParamsIndirect) {
157
15
      Sig.getParamTypes().assign(1, Ptr);
158
15
    }
159
5.42k
    if (AsyncOpt) {
160
      // Async lift returns the packed callback code, or nothing when stackful.
161
7
      Sig.getReturnTypes().clear();
162
7
      if (Canon.hasOption(ComponentCanonOptCode::Callback)) {
163
0
        Sig.getReturnTypes().push_back(I32V);
164
0
      }
165
5.41k
    } else if (ResultsIndirect) {
166
15
      Sig.getReturnTypes().assign(1, Ptr);
167
15
    }
168
    // Required options: lifting params lowers them into the callee's memory.
169
5.42k
    EXPECTED_TRY(CompCtx.requireOptions(
170
5.40k
        Canon,
171
5.40k
        ParamsNeedMemory || ParamsIndirect ||
172
5.40k
            (!AsyncOpt && (ResultsNeedMemory || ResultsIndirect)),
173
5.40k
        ParamsNeedMemory || ParamsIndirect, "canon lift"sv));
174
175
    // The callee must have exactly the flattened core type.
176
5.40k
    const auto *Callee = S.getCoreFunc(Canon.getIndex());
177
5.40k
    if (Callee == nullptr) {
178
20
      spdlog::error(ErrCode::Value::ComponentFunctionIndexOutOfBounds);
179
20
      spdlog::error("    canon lift core function index {} out of bounds."sv,
180
20
                    Canon.getIndex());
181
20
      return Unexpect(ErrCode::Value::ComponentFunctionIndexOutOfBounds);
182
20
    }
183
5.38k
    const auto &CalleeType = Callee->getCompositeType();
184
5.38k
    if (!CalleeType.isFunc() ||
185
5.38k
        CalleeType.getFuncType().getParamTypes() != Sig.getParamTypes()) {
186
1
      spdlog::error(ErrCode::Value::CanonLoweredParamsMismatch);
187
1
      spdlog::error("    canon lift core function does not match the "
188
1
                    "flattened parameters."sv);
189
1
      return Unexpect(ErrCode::Value::CanonLoweredParamsMismatch);
190
1
    }
191
5.38k
    if (CalleeType.getFuncType().getReturnTypes() != Sig.getReturnTypes()) {
192
1
      spdlog::error(ErrCode::Value::CanonLoweredResultsMismatch);
193
1
      spdlog::error("    canon lift core function does not match the "
194
1
                    "flattened results."sv);
195
1
      return Unexpect(ErrCode::Value::CanonLoweredResultsMismatch);
196
1
    }
197
198
    // post-return has type (func (param flat_results)).
199
5.38k
    for (const auto &Opt : Canon.getOptions()) {
200
217
      if (Opt.getCode() != ComponentCanonOptCode::PostReturn) {
201
25
        continue;
202
25
      }
203
192
      const auto *Post = S.getCoreFunc(Opt.getIndex());
204
192
      if (Post == nullptr) {
205
2
        spdlog::error(ErrCode::Value::ComponentFunctionIndexOutOfBounds);
206
2
        spdlog::error("    post-return core function index {} out of bounds."sv,
207
2
                      Opt.getIndex());
208
2
        return Unexpect(ErrCode::Value::ComponentFunctionIndexOutOfBounds);
209
2
      }
210
190
      const auto &PT = Post->getCompositeType();
211
190
      if (!PT.isFunc() ||
212
190
          PT.getFuncType().getParamTypes() != Sig.getReturnTypes() ||
213
189
          !PT.getFuncType().getReturnTypes().empty()) {
214
1
        spdlog::error(ErrCode::Value::CanonPostReturnSignature);
215
1
        spdlog::error(
216
1
            "    post-return must take the lifted core results and return "
217
1
            "nothing."sv);
218
1
        return Unexpect(ErrCode::Value::CanonPostReturnSignature);
219
1
      }
220
190
    }
221
222
5.38k
    S.addFunc(FI);
223
5.38k
    return {};
224
5.38k
  }
225
226
2.35k
  case ComponentCanonOpCode::Lower: {
227
2.35k
    const auto *FI = S.getFunc(Canon.getIndex());
228
2.35k
    if (FI == nullptr) {
229
5
      spdlog::error(ErrCode::Value::ComponentFunctionIndexOutOfBounds);
230
5
      spdlog::error("    canon lower function index {} out of bounds."sv,
231
5
                    Canon.getIndex());
232
5
      return Unexpect(ErrCode::Value::ComponentFunctionIndexOutOfBounds);
233
5
    }
234
2.35k
    EXPECTED_TRY(CompCtx.checkOptions(Canon, false));
235
2.34k
    const bool AsyncOpt = Canon.hasOption(ComponentCanonOptCode::Async);
236
2.34k
    if (FI->FT == nullptr) {
237
0
      spdlog::error(ErrCode::Value::InvalidTypeReference);
238
0
      return Unexpect(ErrCode::Value::InvalidTypeReference);
239
0
    }
240
2.34k
    if (AsyncOpt && !FI->FT->isAsync()) {
241
1
      spdlog::error(ErrCode::Value::CanonAsyncRequiresAsyncType);
242
1
      spdlog::error("    canon lower with `async` needs `(func async ...)`."sv);
243
1
      return Unexpect(ErrCode::Value::CanonAsyncRequiresAsyncType);
244
1
    }
245
246
2.34k
    const ValType Ptr = CompCtx.getCanonPtrType(Canon);
247
2.34k
    EXPECTED_TRY(auto Sig, CompTypes.flattenFuncType(*FI, Ptr, ParamsNeedMemory,
248
2.34k
                                                     ResultsNeedMemory));
249
2.34k
    const bool ParamsIndirect =
250
2.34k
        Sig.getParamTypes().size() >
251
2.34k
        (AsyncOpt ? Component::TypeSystem::MaxFlatAsyncParams
252
2.34k
                  : Component::TypeSystem::MaxFlatParams);
253
2.34k
    const bool ResultsIndirect =
254
2.34k
        AsyncOpt ? !Sig.getReturnTypes().empty()
255
2.34k
                 : Sig.getReturnTypes().size() >
256
2.33k
                       Component::TypeSystem::MaxFlatResults;
257
2.34k
    if (ParamsIndirect) {
258
0
      Sig.getParamTypes().assign(1, Ptr);
259
0
    }
260
2.34k
    if (ResultsIndirect) {
261
      // The caller passes a pointer for the results as the last parameter.
262
2
      Sig.getParamTypes().push_back(Ptr);
263
2
      Sig.getReturnTypes().clear();
264
2
    }
265
2.34k
    if (AsyncOpt) {
266
      // Async lower: the core function returns the packed subtask state.
267
11
      Sig.getReturnTypes().assign(1, I32V);
268
11
    }
269
2.34k
    EXPECTED_TRY(CompCtx.requireOptions(Canon,
270
2.34k
                                        ParamsNeedMemory || ResultsNeedMemory ||
271
2.34k
                                            ParamsIndirect || ResultsIndirect,
272
2.34k
                                        ResultsNeedMemory, "canon lower"sv));
273
2.34k
    return PushCoreFunc(Sig.getParamTypes(), Sig.getReturnTypes());
274
2.34k
  }
275
276
25
  case ComponentCanonOpCode::Resource__new: {
277
25
    EXPECTED_TRY(const auto Rep, LocalResourceRep("resource.new"sv));
278
14
    return PushCoreFunc({Rep}, {I32V});
279
25
  }
280
46
  case ComponentCanonOpCode::Resource__rep: {
281
46
    EXPECTED_TRY(const auto Rep, LocalResourceRep("resource.rep"sv));
282
23
    return PushCoreFunc({I32V}, {Rep});
283
46
  }
284
79
  case ComponentCanonOpCode::Resource__drop:
285
79
    EXPECTED_TRY(GetResourceEntry("resource.drop"sv));
286
70
    return PushCoreFunc({I32V}, {});
287
288
  // Async built-ins take their core signatures from the shared table.
289
64
  case ComponentCanonOpCode::Backpressure__inc:
290
282
  case ComponentCanonOpCode::Backpressure__dec:
291
282
    return PushBuiltin(I32V);
292
261
  case ComponentCanonOpCode::Thread__index:
293
261
    return PushBuiltin(I32V);
294
1.11k
  case ComponentCanonOpCode::Task__return: {
295
    // The results lower as the core parameters; task.return never reallocates.
296
1.11k
    EXPECTED_TRY(CompCtx.checkOptions(Canon, false));
297
1.10k
    if (Canon.hasOption(ComponentCanonOptCode::Realloc)) {
298
0
      spdlog::error(ErrCode::Value::CanonReallocOnBuiltin);
299
0
      spdlog::error("    realloc cannot be specified for task.return."sv);
300
0
      return Unexpect(ErrCode::Value::CanonReallocOnBuiltin);
301
0
    }
302
1.10k
    EXPECTED_TRY(RequireSync());
303
1.10k
    const ValType Ptr = CompCtx.getCanonPtrType(Canon);
304
1.10k
    std::vector<ValType> Params;
305
1.10k
    for (const auto &R : Canon.getResultList()) {
306
1.00k
      const Component::QualValType Q{R.getValType(), &S, nullptr};
307
1.00k
      if (!CompTypes.flattenValType(Q, Params, Ptr)) {
308
13
        spdlog::error(ErrCode::Value::InvalidTypeReference);
309
13
        return Unexpect(ErrCode::Value::InvalidTypeReference);
310
13
      }
311
992
      ParamsNeedMemory = ParamsNeedMemory || CompTypes.needsMemory(Q);
312
992
    }
313
1.09k
    if (Params.size() > Component::TypeSystem::MaxFlatParams) {
314
19
      Params.assign(1, Ptr);
315
19
      ParamsNeedMemory = true;
316
19
    }
317
1.09k
    EXPECTED_TRY(CompCtx.requireOptions(Canon, ParamsNeedMemory, false,
318
1.00k
                                        "task.return"sv));
319
1.00k
    return PushCoreFunc(std::move(Params), {});
320
1.09k
  }
321
91
  case ComponentCanonOpCode::Task__cancel:
322
91
    return PushBuiltin(I32V);
323
1.58k
  case ComponentCanonOpCode::Context__get:
324
1.86k
  case ComponentCanonOpCode::Context__set: {
325
1.86k
    const bool IsGet = Canon.getOpCode() == ComponentCanonOpCode::Context__get;
326
1.86k
    const std::string_view Name = IsGet ? "context.get"sv : "context.set"sv;
327
    // The slot immediate is stored as the constant value, not as an index.
328
1.86k
    if (Canon.getConstVal() >= Component::TypeSystem::MaxContextSlots) {
329
31
      spdlog::error(ErrCode::Value::ComponentContextSlotOutOfBounds);
330
31
      spdlog::error(
331
31
          "    Context slot {} out of bounds, must be less than {}."sv,
332
31
          Canon.getConstVal(), Component::TypeSystem::MaxContextSlots);
333
31
      return Unexpect(ErrCode::Value::ComponentContextSlotOutOfBounds);
334
31
    }
335
1.83k
    const ValType Ty = Canon.getContextType();
336
1.83k
    if (Ty != I32V && Ty != I64V) {
337
9
      spdlog::error(ErrCode::Value::ComponentContextTypeInvalid);
338
9
      spdlog::error("    `{}` only supports `i32` or `i64`."sv, Name);
339
9
      return Unexpect(ErrCode::Value::ComponentContextTypeInvalid);
340
9
    }
341
    // A 64-bit slot is gated the same way a 64-bit resource rep is.
342
1.82k
    if (Ty == I64V && !Conf.hasProposal(Proposal::Memory64)) {
343
0
      spdlog::error(ErrCode::Value::ComponentContextTypeInvalid);
344
0
      spdlog::error(ErrInfo::InfoProposal(Proposal::Memory64));
345
0
      spdlog::error("    a 64-bit `{}` needs the memory64 proposal."sv, Name);
346
0
      return Unexpect(ErrCode::Value::ComponentContextTypeInvalid);
347
0
    }
348
    // The whole component shares one thread-local slot type.
349
1.82k
    auto &Shared = CompCtx.top().ContextType;
350
1.82k
    if (Shared.has_value() && *Shared != Ty) {
351
1
      spdlog::error(ErrCode::Value::ComponentContextTypeMismatch);
352
1
      spdlog::error("    `{}` type must match previous context type."sv, Name);
353
1
      return Unexpect(ErrCode::Value::ComponentContextTypeMismatch);
354
1
    }
355
1.82k
    Shared = Ty;
356
1.82k
    if (IsGet) {
357
1.55k
      return PushCoreFunc({}, {Ty});
358
1.55k
    }
359
276
    return PushCoreFunc({Ty}, {});
360
1.82k
  }
361
81
  case ComponentCanonOpCode::Yield:
362
81
    return PushBuiltin(I32V);
363
88
  case ComponentCanonOpCode::Subtask__cancel:
364
88
    return PushBuiltin(I32V);
365
776
  case ComponentCanonOpCode::Subtask__drop:
366
776
    return PushBuiltin(I32V);
367
90
  case ComponentCanonOpCode::Stream__new:
368
90
    EXPECTED_TRY(CheckTypeImmediate(true));
369
66
    return PushBuiltin(I32V);
370
286
  case ComponentCanonOpCode::Stream__read:
371
640
  case ComponentCanonOpCode::Stream__write: {
372
640
    EXPECTED_TRY(const auto *Entry, CheckTypeImmediate(true));
373
622
    const auto &Elem = Entry->getDefValType()->getStream().ValTy;
374
    // Reading a payload that allocates needs realloc; writing never does.
375
622
    const bool NeedRealloc =
376
622
        Canon.getOpCode() == ComponentCanonOpCode::Stream__read &&
377
277
        Elem.has_value() &&
378
1
        CompTypes.needsMemory({*Elem, Entry->Home, Entry->Remap});
379
622
    EXPECTED_TRY(BuiltinOptions(Elem.has_value(), NeedRealloc));
380
621
    return PushBuiltin(CompCtx.getCanonPtrType(Canon));
381
622
  }
382
480
  case ComponentCanonOpCode::Stream__cancel_read:
383
570
  case ComponentCanonOpCode::Stream__cancel_write:
384
570
    EXPECTED_TRY(CheckTypeImmediate(true));
385
539
    return PushBuiltin(I32V);
386
109
  case ComponentCanonOpCode::Stream__drop_readable:
387
752
  case ComponentCanonOpCode::Stream__drop_writable:
388
752
    EXPECTED_TRY(CheckTypeImmediate(true));
389
715
    return PushBuiltin(I32V);
390
26.6k
  case ComponentCanonOpCode::Future__new:
391
26.6k
    EXPECTED_TRY(CheckTypeImmediate(false));
392
26.6k
    return PushBuiltin(I32V);
393
133
  case ComponentCanonOpCode::Future__read:
394
215
  case ComponentCanonOpCode::Future__write: {
395
215
    EXPECTED_TRY(const auto *Entry, CheckTypeImmediate(false));
396
201
    const auto &Elem = Entry->getDefValType()->getFuture().ValTy;
397
    // Reading a payload that allocates needs realloc; writing never does.
398
201
    const bool NeedRealloc =
399
201
        Canon.getOpCode() == ComponentCanonOpCode::Future__read &&
400
128
        Elem.has_value() &&
401
0
        CompTypes.needsMemory({*Elem, Entry->Home, Entry->Remap});
402
201
    EXPECTED_TRY(BuiltinOptions(Elem.has_value(), NeedRealloc));
403
201
    return PushBuiltin(CompCtx.getCanonPtrType(Canon));
404
201
  }
405
96
  case ComponentCanonOpCode::Future__cancel_read:
406
339
  case ComponentCanonOpCode::Future__cancel_write:
407
339
    EXPECTED_TRY(CheckTypeImmediate(false));
408
314
    return PushBuiltin(I32V);
409
154
  case ComponentCanonOpCode::Future__drop_readable:
410
1.37k
  case ComponentCanonOpCode::Future__drop_writable:
411
1.37k
    EXPECTED_TRY(CheckTypeImmediate(false));
412
1.33k
    return PushBuiltin(I32V);
413
8
  case ComponentCanonOpCode::Error_context__new:
414
8
    EXPECTED_TRY(BuiltinOptions(true, false));
415
0
    EXPECTED_TRY(RequireSync());
416
0
    return PushBuiltin(CompCtx.getCanonPtrType(Canon));
417
3
  case ComponentCanonOpCode::Error_context__debug_message:
418
3
    EXPECTED_TRY(BuiltinOptions(true, true));
419
0
    EXPECTED_TRY(RequireSync());
420
0
    return PushBuiltin(CompCtx.getCanonPtrType(Canon));
421
138
  case ComponentCanonOpCode::Error_context__drop:
422
138
    return PushBuiltin(I32V);
423
269
  case ComponentCanonOpCode::Waitable_set__new:
424
269
    return PushBuiltin(I32V);
425
16
  case ComponentCanonOpCode::Waitable_set__wait:
426
19
  case ComponentCanonOpCode::Waitable_set__poll: {
427
19
    if (Canon.getIndex() >= S.CoreMemories.size()) {
428
19
      spdlog::error(ErrCode::Value::ComponentMemoryIndexOutOfBounds);
429
19
      spdlog::error("    Canonical built-in memory index {} out of bounds."sv,
430
19
                    Canon.getIndex());
431
19
      return Unexpect(ErrCode::Value::ComponentMemoryIndexOutOfBounds);
432
19
    }
433
    // The payload address carries the address type of the given memory.
434
0
    const auto *Mem = S.CoreMemories[Canon.getIndex()];
435
0
    const ValType Addr =
436
0
        ValType(Mem != nullptr && Mem->getLimit().is64() ? TypeCode::I64
437
0
                                                         : TypeCode::I32);
438
0
    return PushBuiltin(Addr);
439
19
  }
440
116
  case ComponentCanonOpCode::Waitable_set__drop:
441
116
    return PushBuiltin(I32V);
442
270
  case ComponentCanonOpCode::Waitable__join:
443
270
    return PushBuiltin(I32V);
444
8
  case ComponentCanonOpCode::Thread__new_indirect: {
445
    // The start type must be (func (param i32)) and the target a core table.
446
8
    const auto *CT = S.getCoreType(Canon.getIndex());
447
8
    const bool TypeOk =
448
8
        CT != nullptr && CT->Func != nullptr &&
449
0
        CT->Func->getCompositeType().isFunc() &&
450
0
        CT->Func->getCompositeType().getFuncType().getParamTypes() ==
451
0
            std::vector<ValType>{I32V} &&
452
0
        CT->Func->getCompositeType().getFuncType().getReturnTypes().empty();
453
8
    if (!TypeOk) {
454
8
      spdlog::error(ErrCode::Value::InvalidTypeReference);
455
8
      spdlog::error(
456
8
          "    thread.new-indirect start type must be (func (param i32))."sv);
457
8
      return Unexpect(ErrCode::Value::InvalidTypeReference);
458
8
    }
459
0
    if (Canon.getTargetIndex() >= S.CoreTables.size()) {
460
0
      spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
461
0
      spdlog::error("    thread.new-indirect table index {} out of bounds."sv,
462
0
                    Canon.getTargetIndex());
463
0
      return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
464
0
    }
465
    // The thread index carries the address type of the table.
466
0
    const auto *Tab = S.CoreTables[Canon.getTargetIndex()];
467
0
    return PushBuiltin(ValType(Tab != nullptr && Tab->getLimit().is64()
468
0
                                   ? TypeCode::I64
469
0
                                   : TypeCode::I32));
470
0
  }
471
30
  case ComponentCanonOpCode::Thread__resume_later:
472
30
    return PushBuiltin(I32V);
473
704
  case ComponentCanonOpCode::Thread__suspend:
474
704
    return PushBuiltin(I32V);
475
92
  case ComponentCanonOpCode::Thread__suspend_then_resume:
476
573
  case ComponentCanonOpCode::Thread__yield_then_resume:
477
937
  case ComponentCanonOpCode::Thread__suspend_then_promote:
478
1.02k
  case ComponentCanonOpCode::Thread__yield_then_promote:
479
1.02k
    return PushBuiltin(I32V);
480
18
  default:
481
18
    spdlog::error(ErrCode::Value::ComponentNotImplValidator);
482
18
    spdlog::error("    canonical built-in {} is not supported yet."sv,
483
18
                  static_cast<uint32_t>(Canon.getOpCode()));
484
18
    return Unexpect(ErrCode::Value::ComponentNotImplValidator);
485
45.8k
  }
486
45.8k
}
487
488
} // namespace Validator
489
} // namespace WasmEdge