Coverage Report

Created: 2026-01-22 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/crates/wasmparser/src/readers/component/canonicals.rs
Line
Count
Source
1
use crate::limits::MAX_WASM_CANONICAL_OPTIONS;
2
use crate::prelude::*;
3
use crate::{BinaryReader, ComponentValType, FromReader, Result, SectionLimited};
4
5
/// Represents options for component functions.
6
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7
pub enum CanonicalOption {
8
    /// The string types in the function signature are UTF-8 encoded.
9
    UTF8,
10
    /// The string types in the function signature are UTF-16 encoded.
11
    UTF16,
12
    /// The string types in the function signature are compact UTF-16 encoded.
13
    CompactUTF16,
14
    /// The memory to use if the lifting or lowering of a function requires memory access.
15
    ///
16
    /// The value is an index to a core memory.
17
    Memory(u32),
18
    /// The realloc function to use if the lifting or lowering of a function requires memory
19
    /// allocation.
20
    ///
21
    /// The value is an index to a core function of type `(func (param i32 i32 i32 i32) (result i32))`.
22
    Realloc(u32),
23
    /// The post-return function to use if the lifting of a function requires
24
    /// cleanup after the function returns.
25
    PostReturn(u32),
26
    /// Indicates that specified function should be lifted or lowered using the `async` ABI.
27
    Async,
28
    /// The function to use if the async lifting of a function should receive task/stream/future progress events
29
    /// using a callback.
30
    Callback(u32),
31
    /// The core function type to lower this component function to.
32
    CoreType(u32),
33
    /// Use the GC version of the canonical ABI.
34
    Gc,
35
}
36
37
/// Represents a canonical function in a WebAssembly component.
38
#[derive(Debug, Clone, Eq, PartialEq)]
39
pub enum CanonicalFunction {
40
    /// The function lifts a core WebAssembly function to the canonical ABI.
41
    Lift {
42
        /// The index of the core WebAssembly function to lift.
43
        core_func_index: u32,
44
        /// The index of the lifted function's type.
45
        type_index: u32,
46
        /// The canonical options for the function.
47
        options: Box<[CanonicalOption]>,
48
    },
49
    /// The function lowers a canonical ABI function to a core WebAssembly function.
50
    Lower {
51
        /// The index of the function to lower.
52
        func_index: u32,
53
        /// The canonical options for the function.
54
        options: Box<[CanonicalOption]>,
55
    },
56
    /// A function which creates a new owned handle to a resource.
57
    ResourceNew {
58
        /// The type index of the resource that's being created.
59
        resource: u32,
60
    },
61
    /// A function which is used to drop resource handles of the specified type.
62
    ResourceDrop {
63
        /// The type index of the resource that's being dropped.
64
        resource: u32,
65
    },
66
    /// Same as `ResourceDrop`, but implements the `async` ABI.
67
    ResourceDropAsync {
68
        /// The type index of the resource that's being dropped.
69
        resource: u32,
70
    },
71
    /// A function which returns the underlying i32-based representation of the
72
    /// specified resource.
73
    ResourceRep {
74
        /// The type index of the resource that's being accessed.
75
        resource: u32,
76
    },
77
    /// A function which spawns a new thread by invoking the shared function.
78
    ThreadSpawnRef {
79
        /// The index of the function type to spawn.
80
        func_ty_index: u32,
81
    },
82
    /// A function which spawns a new thread by invoking the shared function
83
    /// passed as an index into a `funcref` table.
84
    ThreadSpawnIndirect {
85
        /// The index of the function type to spawn.
86
        func_ty_index: u32,
87
        /// The index of the table to use for the indirect spawn.
88
        table_index: u32,
89
    },
90
    /// A function which returns the number of threads that can be expected to
91
    /// execute concurrently
92
    ThreadAvailableParallelism,
93
    /// A function which tells the host to enable backpressure by incrementing
94
    /// the component's counter by 1.
95
    BackpressureInc,
96
    /// A function which tells the host to disable backpressure by decrementing
97
    /// the component's counter by 1.
98
    BackpressureDec,
99
    /// A function which returns a result to the caller of a lifted export
100
    /// function.  This allows the callee to continue executing after returning
101
    /// a result.
102
    TaskReturn {
103
        /// The result type, if any.
104
        result: Option<ComponentValType>,
105
        /// The canonical options for the function.
106
        options: Box<[CanonicalOption]>,
107
    },
108
    /// A function to acknowledge cancellation of the current task.
109
    TaskCancel,
110
    /// A `context.get` intrinsic for the `i`th slot of task-local storage.
111
    ContextGet(u32),
112
    /// A `context.set` intrinsic for the `i`th slot of task-local storage.
113
    ContextSet(u32),
114
    /// A function which yields control to the host so that other tasks are able
115
    /// to make progress, if any.
116
    ThreadYield {
117
        /// If `true`, indicates the caller instance maybe reentered.
118
        cancellable: bool,
119
    },
120
    /// A function to drop a specified task which has completed.
121
    SubtaskDrop,
122
    /// A function to cancel an in-progress task.
123
    SubtaskCancel {
124
        /// If `false`, block until cancellation completes rather than return
125
        /// `BLOCKED`.
126
        async_: bool,
127
    },
128
    /// A function to create a new `stream` handle of the specified type.
129
    StreamNew {
130
        /// The `stream` type to instantiate.
131
        ty: u32,
132
    },
133
    /// A function to read from a `stream` of the specified type.
134
    StreamRead {
135
        /// The `stream` type to expect.
136
        ty: u32,
137
        /// Any options (e.g. string encoding) to use when storing values to
138
        /// memory.
139
        options: Box<[CanonicalOption]>,
140
    },
141
    /// A function to write to a `stream` of the specified type.
142
    StreamWrite {
143
        /// The `stream` type to expect.
144
        ty: u32,
145
        /// Any options (e.g. string encoding) to use when loading values from
146
        /// memory.
147
        options: Box<[CanonicalOption]>,
148
    },
149
    /// A function to cancel an in-progress read from a `stream` of the
150
    /// specified type.
151
    StreamCancelRead {
152
        /// The `stream` type to expect.
153
        ty: u32,
154
        /// If `false`, block until cancellation completes rather than return
155
        /// `BLOCKED`.
156
        async_: bool,
157
    },
158
    /// A function to cancel an in-progress write to a `stream` of the specified
159
    /// type.
160
    StreamCancelWrite {
161
        /// The `stream` type to expect.
162
        ty: u32,
163
        /// If `false`, block until cancellation completes rather than return
164
        /// `BLOCKED`.
165
        async_: bool,
166
    },
167
    /// A function to drop the readable end of a `stream` of the specified
168
    /// type.
169
    StreamDropReadable {
170
        /// The `stream` type to expect.
171
        ty: u32,
172
    },
173
    /// A function to drop the writable end of a `stream` of the specified
174
    /// type.
175
    StreamDropWritable {
176
        /// The `stream` type to expect.
177
        ty: u32,
178
    },
179
    /// A function to create a new `future` handle of the specified type.
180
    FutureNew {
181
        /// The `future` type to instantiate.
182
        ty: u32,
183
    },
184
    /// A function to read from a `future` of the specified type.
185
    FutureRead {
186
        /// The `future` type to expect.
187
        ty: u32,
188
        /// Any options (e.g. string encoding) to use when storing values to
189
        /// memory.
190
        options: Box<[CanonicalOption]>,
191
    },
192
    /// A function to write to a `future` of the specified type.
193
    FutureWrite {
194
        /// The `future` type to expect.
195
        ty: u32,
196
        /// Any options (e.g. string encoding) to use when loading values from
197
        /// memory.
198
        options: Box<[CanonicalOption]>,
199
    },
200
    /// A function to cancel an in-progress read from a `future` of the
201
    /// specified type.
202
    FutureCancelRead {
203
        /// The `future` type to expect.
204
        ty: u32,
205
        /// If `false`, block until cancellation completes rather than return
206
        /// `BLOCKED`.
207
        async_: bool,
208
    },
209
    /// A function to cancel an in-progress write to a `future` of the specified
210
    /// type.
211
    FutureCancelWrite {
212
        /// The `future` type to expect.
213
        ty: u32,
214
        /// If `false`, block until cancellation completes rather than return
215
        /// `BLOCKED`.
216
        async_: bool,
217
    },
218
    /// A function to drop the readable end of a `future` of the specified
219
    /// type.
220
    FutureDropReadable {
221
        /// The `future` type to expect.
222
        ty: u32,
223
    },
224
    /// A function to drop the writable end of a `future` of the specified
225
    /// type.
226
    FutureDropWritable {
227
        /// The `future` type to expect.
228
        ty: u32,
229
    },
230
    /// A function to create a new `error-context` with a specified debug
231
    /// message.
232
    ErrorContextNew {
233
        /// String encoding, memory, etc. to use when loading debug message.
234
        options: Box<[CanonicalOption]>,
235
    },
236
    /// A function to get the debug message for a specified `error-context`.
237
    ///
238
    /// Note that the debug message might not necessarily match what was passed
239
    /// to `error.new`.
240
    ErrorContextDebugMessage {
241
        /// String encoding, memory, etc. to use when storing debug message.
242
        options: Box<[CanonicalOption]>,
243
    },
244
    /// A function to drop a specified `error-context`.
245
    ErrorContextDrop,
246
    /// A function to create a new `waitable-set`.
247
    WaitableSetNew,
248
    /// A function to block on the next item within a `waitable-set`.
249
    WaitableSetWait {
250
        /// Whether or not the guest can be reentered while calling this
251
        /// function.
252
        cancellable: bool,
253
        /// Which memory the results of this operation are stored in.
254
        memory: u32,
255
    },
256
    /// A function to check if any items are ready within a `waitable-set`.
257
    WaitableSetPoll {
258
        /// Whether or not the guest can be reentered while calling this
259
        /// function.
260
        cancellable: bool,
261
        /// Which memory the results of this operation are stored in.
262
        memory: u32,
263
    },
264
    /// A function to drop a `waitable-set`.
265
    WaitableSetDrop,
266
    /// A function to add an item to a `waitable-set`.
267
    WaitableJoin,
268
    /// A function to get the index of the current thread.
269
    ThreadIndex,
270
    /// A function to create a new thread with the specified start function.
271
    ThreadNewIndirect {
272
        /// The index of the function type to use as the start function.
273
        func_ty_index: u32,
274
        /// The index of the table to use.
275
        table_index: u32,
276
    },
277
    /// A function to suspend the current thread and switch to the given thread.
278
    ThreadSwitchTo {
279
        /// Whether or not the thread can be cancelled while awaiting resumption.
280
        cancellable: bool,
281
    },
282
    /// A function to suspend the current thread, immediately yielding to any transitive async-lowered calling component.
283
    ThreadSuspend {
284
        /// Whether or not the thread can be cancelled while suspended.
285
        cancellable: bool,
286
    },
287
    /// A function to schedule the given thread to be resumed later.
288
    ThreadResumeLater,
289
    /// A function to suspend the current thread and switch to the given thread.
290
    ThreadYieldTo {
291
        /// Whether or not the thread can be cancelled while yielding.
292
        cancellable: bool,
293
    },
294
}
295
296
/// A reader for the canonical section of a WebAssembly component.
297
pub type ComponentCanonicalSectionReader<'a> = SectionLimited<'a, CanonicalFunction>;
298
299
impl<'a> FromReader<'a> for CanonicalFunction {
300
26.0k
    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<CanonicalFunction> {
301
26.0k
        Ok(match reader.read_u8()? {
302
2.29k
            0x00 => match reader.read_u8()? {
303
                0x00 => CanonicalFunction::Lift {
304
2.29k
                    core_func_index: reader.read_var_u32()?,
305
2.29k
                    options: read_opts(reader)?,
306
2.29k
                    type_index: reader.read_var_u32()?,
307
                },
308
0
                x => return reader.invalid_leading_byte(x, "canonical function lift"),
309
            },
310
3.81k
            0x01 => match reader.read_u8()? {
311
                0x00 => CanonicalFunction::Lower {
312
3.81k
                    func_index: reader.read_var_u32()?,
313
3.81k
                    options: read_opts(reader)?,
314
                },
315
0
                x => return reader.invalid_leading_byte(x, "canonical function lower"),
316
            },
317
            0x02 => CanonicalFunction::ResourceNew {
318
170
                resource: reader.read()?,
319
            },
320
            0x03 => CanonicalFunction::ResourceDrop {
321
720
                resource: reader.read()?,
322
            },
323
            0x07 => CanonicalFunction::ResourceDropAsync {
324
0
                resource: reader.read()?,
325
            },
326
            0x04 => CanonicalFunction::ResourceRep {
327
170
                resource: reader.read()?,
328
            },
329
1.25k
            0x24 => CanonicalFunction::BackpressureInc,
330
1.25k
            0x25 => CanonicalFunction::BackpressureDec,
331
            0x09 => CanonicalFunction::TaskReturn {
332
470
                result: crate::read_resultlist(reader)?,
333
470
                options: read_opts(reader)?,
334
            },
335
1.25k
            0x0a => match reader.read_u8()? {
336
1.25k
                0x7f => CanonicalFunction::ContextGet(reader.read_var_u32()?),
337
0
                x => return reader.invalid_leading_byte(x, "context.get intrinsic type"),
338
            },
339
1.25k
            0x0b => match reader.read_u8()? {
340
1.25k
                0x7f => CanonicalFunction::ContextSet(reader.read_var_u32()?),
341
0
                x => return reader.invalid_leading_byte(x, "context.set intrinsic type"),
342
            },
343
            0x0c => CanonicalFunction::ThreadYield {
344
1.25k
                cancellable: reader.read()?,
345
            },
346
1.25k
            0x0d => CanonicalFunction::SubtaskDrop,
347
244
            0x0e => CanonicalFunction::StreamNew { ty: reader.read()? },
348
            0x0f => CanonicalFunction::StreamRead {
349
244
                ty: reader.read()?,
350
244
                options: read_opts(reader)?,
351
            },
352
            0x10 => CanonicalFunction::StreamWrite {
353
244
                ty: reader.read()?,
354
244
                options: read_opts(reader)?,
355
            },
356
            0x11 => CanonicalFunction::StreamCancelRead {
357
244
                ty: reader.read()?,
358
244
                async_: reader.read()?,
359
            },
360
            0x12 => CanonicalFunction::StreamCancelWrite {
361
244
                ty: reader.read()?,
362
244
                async_: reader.read()?,
363
            },
364
244
            0x13 => CanonicalFunction::StreamDropReadable { ty: reader.read()? },
365
244
            0x14 => CanonicalFunction::StreamDropWritable { ty: reader.read()? },
366
46
            0x15 => CanonicalFunction::FutureNew { ty: reader.read()? },
367
            0x16 => CanonicalFunction::FutureRead {
368
46
                ty: reader.read()?,
369
46
                options: read_opts(reader)?,
370
            },
371
            0x17 => CanonicalFunction::FutureWrite {
372
46
                ty: reader.read()?,
373
46
                options: read_opts(reader)?,
374
            },
375
            0x18 => CanonicalFunction::FutureCancelRead {
376
46
                ty: reader.read()?,
377
46
                async_: reader.read()?,
378
            },
379
            0x19 => CanonicalFunction::FutureCancelWrite {
380
46
                ty: reader.read()?,
381
46
                async_: reader.read()?,
382
            },
383
46
            0x1a => CanonicalFunction::FutureDropReadable { ty: reader.read()? },
384
46
            0x1b => CanonicalFunction::FutureDropWritable { ty: reader.read()? },
385
            0x1c => CanonicalFunction::ErrorContextNew {
386
0
                options: read_opts(reader)?,
387
            },
388
            0x1d => CanonicalFunction::ErrorContextDebugMessage {
389
0
                options: read_opts(reader)?,
390
            },
391
0
            0x1e => CanonicalFunction::ErrorContextDrop,
392
393
1.25k
            0x1f => CanonicalFunction::WaitableSetNew,
394
            0x20 => CanonicalFunction::WaitableSetWait {
395
1.25k
                cancellable: reader.read()?,
396
1.25k
                memory: reader.read()?,
397
            },
398
            0x21 => CanonicalFunction::WaitableSetPoll {
399
1.25k
                cancellable: reader.read()?,
400
1.25k
                memory: reader.read()?,
401
            },
402
1.25k
            0x22 => CanonicalFunction::WaitableSetDrop,
403
1.25k
            0x23 => CanonicalFunction::WaitableJoin,
404
0
            0x26 => CanonicalFunction::ThreadIndex,
405
            0x27 => CanonicalFunction::ThreadNewIndirect {
406
0
                func_ty_index: reader.read()?,
407
0
                table_index: reader.read()?,
408
            },
409
            0x28 => CanonicalFunction::ThreadSwitchTo {
410
0
                cancellable: reader.read()?,
411
            },
412
            0x29 => CanonicalFunction::ThreadSuspend {
413
0
                cancellable: reader.read()?,
414
            },
415
0
            0x2a => CanonicalFunction::ThreadResumeLater,
416
            0x2b => CanonicalFunction::ThreadYieldTo {
417
0
                cancellable: reader.read()?,
418
            },
419
            0x06 => CanonicalFunction::SubtaskCancel {
420
1.25k
                async_: reader.read()?,
421
            },
422
1.25k
            0x05 => CanonicalFunction::TaskCancel,
423
            0x40 => CanonicalFunction::ThreadSpawnRef {
424
0
                func_ty_index: reader.read()?,
425
            },
426
            0x41 => CanonicalFunction::ThreadSpawnIndirect {
427
0
                func_ty_index: reader.read()?,
428
0
                table_index: reader.read()?,
429
            },
430
0
            0x42 => CanonicalFunction::ThreadAvailableParallelism,
431
0
            x => return reader.invalid_leading_byte(x, "canonical function"),
432
        })
433
26.0k
    }
434
}
435
436
7.16k
fn read_opts(reader: &mut BinaryReader<'_>) -> Result<Box<[CanonicalOption]>> {
437
7.16k
    reader
438
7.16k
        .read_iter(MAX_WASM_CANONICAL_OPTIONS, "canonical options")?
439
7.16k
        .collect::<Result<_>>()
440
7.16k
}
441
442
impl<'a> FromReader<'a> for CanonicalOption {
443
5.60k
    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
444
5.60k
        Ok(match reader.read_u8()? {
445
294
            0x00 => CanonicalOption::UTF8,
446
0
            0x01 => CanonicalOption::UTF16,
447
0
            0x02 => CanonicalOption::CompactUTF16,
448
1.84k
            0x03 => CanonicalOption::Memory(reader.read_var_u32()?),
449
258
            0x04 => CanonicalOption::Realloc(reader.read_var_u32()?),
450
1.82k
            0x05 => CanonicalOption::PostReturn(reader.read_var_u32()?),
451
1.03k
            0x06 => CanonicalOption::Async,
452
344
            0x07 => CanonicalOption::Callback(reader.read_var_u32()?),
453
0
            0x08 => CanonicalOption::CoreType(reader.read_var_u32()?),
454
0
            0x09 => CanonicalOption::Gc,
455
0
            x => return reader.invalid_leading_byte(x, "canonical option"),
456
        })
457
5.60k
    }
458
}