Coverage Report

Created: 2025-10-12 07:32

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 or disable backpressure for
94
    /// the caller's instance.
95
    BackpressureSet,
96
    /// A function which tells the host to enable backpressure by incrementing
97
    /// the component's counter by 1.
98
    BackpressureInc,
99
    /// A function which tells the host to disable backpressure by decrementing
100
    /// the component's counter by 1.
101
    BackpressureDec,
102
    /// A function which returns a result to the caller of a lifted export
103
    /// function.  This allows the callee to continue executing after returning
104
    /// a result.
105
    TaskReturn {
106
        /// The result type, if any.
107
        result: Option<ComponentValType>,
108
        /// The canonical options for the function.
109
        options: Box<[CanonicalOption]>,
110
    },
111
    /// A function to acknowledge cancellation of the current task.
112
    TaskCancel,
113
    /// A `context.get` intrinsic for the `i`th slot of task-local storage.
114
    ContextGet(u32),
115
    /// A `context.set` intrinsic for the `i`th slot of task-local storage.
116
    ContextSet(u32),
117
    /// A function which yields control to the host so that other tasks are able
118
    /// to make progress, if any.
119
    ThreadYield {
120
        /// If `true`, indicates the caller instance maybe reentered.
121
        cancellable: bool,
122
    },
123
    /// A function to drop a specified task which has completed.
124
    SubtaskDrop,
125
    /// A function to cancel an in-progress task.
126
    SubtaskCancel {
127
        /// If `false`, block until cancellation completes rather than return
128
        /// `BLOCKED`.
129
        async_: bool,
130
    },
131
    /// A function to create a new `stream` handle of the specified type.
132
    StreamNew {
133
        /// The `stream` type to instantiate.
134
        ty: u32,
135
    },
136
    /// A function to read from a `stream` of the specified type.
137
    StreamRead {
138
        /// The `stream` type to expect.
139
        ty: u32,
140
        /// Any options (e.g. string encoding) to use when storing values to
141
        /// memory.
142
        options: Box<[CanonicalOption]>,
143
    },
144
    /// A function to write to a `stream` of the specified type.
145
    StreamWrite {
146
        /// The `stream` type to expect.
147
        ty: u32,
148
        /// Any options (e.g. string encoding) to use when loading values from
149
        /// memory.
150
        options: Box<[CanonicalOption]>,
151
    },
152
    /// A function to cancel an in-progress read from a `stream` of the
153
    /// specified type.
154
    StreamCancelRead {
155
        /// The `stream` type to expect.
156
        ty: u32,
157
        /// If `false`, block until cancellation completes rather than return
158
        /// `BLOCKED`.
159
        async_: bool,
160
    },
161
    /// A function to cancel an in-progress write to a `stream` of the specified
162
    /// type.
163
    StreamCancelWrite {
164
        /// The `stream` type to expect.
165
        ty: u32,
166
        /// If `false`, block until cancellation completes rather than return
167
        /// `BLOCKED`.
168
        async_: bool,
169
    },
170
    /// A function to drop the readable end of a `stream` of the specified
171
    /// type.
172
    StreamDropReadable {
173
        /// The `stream` type to expect.
174
        ty: u32,
175
    },
176
    /// A function to drop the writable end of a `stream` of the specified
177
    /// type.
178
    StreamDropWritable {
179
        /// The `stream` type to expect.
180
        ty: u32,
181
    },
182
    /// A function to create a new `future` handle of the specified type.
183
    FutureNew {
184
        /// The `future` type to instantiate.
185
        ty: u32,
186
    },
187
    /// A function to read from a `future` of the specified type.
188
    FutureRead {
189
        /// The `future` type to expect.
190
        ty: u32,
191
        /// Any options (e.g. string encoding) to use when storing values to
192
        /// memory.
193
        options: Box<[CanonicalOption]>,
194
    },
195
    /// A function to write to a `future` of the specified type.
196
    FutureWrite {
197
        /// The `future` type to expect.
198
        ty: u32,
199
        /// Any options (e.g. string encoding) to use when loading values from
200
        /// memory.
201
        options: Box<[CanonicalOption]>,
202
    },
203
    /// A function to cancel an in-progress read from a `future` of the
204
    /// specified type.
205
    FutureCancelRead {
206
        /// The `future` type to expect.
207
        ty: u32,
208
        /// If `false`, block until cancellation completes rather than return
209
        /// `BLOCKED`.
210
        async_: bool,
211
    },
212
    /// A function to cancel an in-progress write to a `future` of the specified
213
    /// type.
214
    FutureCancelWrite {
215
        /// The `future` type to expect.
216
        ty: u32,
217
        /// If `false`, block until cancellation completes rather than return
218
        /// `BLOCKED`.
219
        async_: bool,
220
    },
221
    /// A function to drop the readable end of a `future` of the specified
222
    /// type.
223
    FutureDropReadable {
224
        /// The `future` type to expect.
225
        ty: u32,
226
    },
227
    /// A function to drop the writable end of a `future` of the specified
228
    /// type.
229
    FutureDropWritable {
230
        /// The `future` type to expect.
231
        ty: u32,
232
    },
233
    /// A function to create a new `error-context` with a specified debug
234
    /// message.
235
    ErrorContextNew {
236
        /// String encoding, memory, etc. to use when loading debug message.
237
        options: Box<[CanonicalOption]>,
238
    },
239
    /// A function to get the debug message for a specified `error-context`.
240
    ///
241
    /// Note that the debug message might not necessarily match what was passed
242
    /// to `error.new`.
243
    ErrorContextDebugMessage {
244
        /// String encoding, memory, etc. to use when storing debug message.
245
        options: Box<[CanonicalOption]>,
246
    },
247
    /// A function to drop a specified `error-context`.
248
    ErrorContextDrop,
249
    /// A function to create a new `waitable-set`.
250
    WaitableSetNew,
251
    /// A function to block on the next item within a `waitable-set`.
252
    WaitableSetWait {
253
        /// Whether or not the guest can be reentered while calling this
254
        /// function.
255
        cancellable: bool,
256
        /// Which memory the results of this operation are stored in.
257
        memory: u32,
258
    },
259
    /// A function to check if any items are ready within a `waitable-set`.
260
    WaitableSetPoll {
261
        /// Whether or not the guest can be reentered while calling this
262
        /// function.
263
        cancellable: bool,
264
        /// Which memory the results of this operation are stored in.
265
        memory: u32,
266
    },
267
    /// A function to drop a `waitable-set`.
268
    WaitableSetDrop,
269
    /// A function to add an item to a `waitable-set`.
270
    WaitableJoin,
271
    /// A function to get the index of the current thread.
272
    ThreadIndex,
273
    /// A function to create a new thread with the specified start function.
274
    ThreadNewIndirect {
275
        /// The index of the function type to use as the start function.
276
        func_ty_index: u32,
277
        /// The index of the table to use.
278
        table_index: u32,
279
    },
280
    /// A function to suspend the current thread and switch to the given thread.
281
    ThreadSwitchTo {
282
        /// Whether or not the thread can be cancelled while awaiting resumption.
283
        cancellable: bool,
284
    },
285
    /// A function to suspend the current thread, immediately yielding to any transitive async-lowered calling component.
286
    ThreadSuspend {
287
        /// Whether or not the thread can be cancelled while suspended.
288
        cancellable: bool,
289
    },
290
    /// A function to schedule the given thread to be resumed later.
291
    ThreadResumeLater,
292
    /// A function to suspend the current thread and switch to the given thread.
293
    ThreadYieldTo {
294
        /// Whether or not the thread can be cancelled while yielding.
295
        cancellable: bool,
296
    },
297
}
298
299
/// A reader for the canonical section of a WebAssembly component.
300
pub type ComponentCanonicalSectionReader<'a> = SectionLimited<'a, CanonicalFunction>;
301
302
impl<'a> FromReader<'a> for CanonicalFunction {
303
19.8k
    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<CanonicalFunction> {
304
19.8k
        Ok(match reader.read_u8()? {
305
2.26k
            0x00 => match reader.read_u8()? {
306
                0x00 => CanonicalFunction::Lift {
307
2.26k
                    core_func_index: reader.read_var_u32()?,
308
2.26k
                    options: read_opts(reader)?,
309
2.26k
                    type_index: reader.read_var_u32()?,
310
                },
311
0
                x => return reader.invalid_leading_byte(x, "canonical function lift"),
312
            },
313
3.26k
            0x01 => match reader.read_u8()? {
314
                0x00 => CanonicalFunction::Lower {
315
3.26k
                    func_index: reader.read_var_u32()?,
316
3.26k
                    options: read_opts(reader)?,
317
                },
318
0
                x => return reader.invalid_leading_byte(x, "canonical function lower"),
319
            },
320
            0x02 => CanonicalFunction::ResourceNew {
321
150
                resource: reader.read()?,
322
            },
323
            0x03 => CanonicalFunction::ResourceDrop {
324
630
                resource: reader.read()?,
325
            },
326
            0x07 => CanonicalFunction::ResourceDropAsync {
327
0
                resource: reader.read()?,
328
            },
329
            0x04 => CanonicalFunction::ResourceRep {
330
156
                resource: reader.read()?,
331
            },
332
834
            0x08 => CanonicalFunction::BackpressureSet,
333
834
            0x24 => CanonicalFunction::BackpressureInc,
334
814
            0x25 => CanonicalFunction::BackpressureDec,
335
            0x09 => CanonicalFunction::TaskReturn {
336
330
                result: crate::read_resultlist(reader)?,
337
330
                options: read_opts(reader)?,
338
            },
339
844
            0x0a => match reader.read_u8()? {
340
844
                0x7f => CanonicalFunction::ContextGet(reader.read_var_u32()?),
341
0
                x => return reader.invalid_leading_byte(x, "context.get intrinsic type"),
342
            },
343
872
            0x0b => match reader.read_u8()? {
344
872
                0x7f => CanonicalFunction::ContextSet(reader.read_var_u32()?),
345
0
                x => return reader.invalid_leading_byte(x, "context.set intrinsic type"),
346
            },
347
            0x0c => CanonicalFunction::ThreadYield {
348
866
                cancellable: reader.read()?,
349
            },
350
832
            0x0d => CanonicalFunction::SubtaskDrop,
351
176
            0x0e => CanonicalFunction::StreamNew { ty: reader.read()? },
352
            0x0f => CanonicalFunction::StreamRead {
353
182
                ty: reader.read()?,
354
182
                options: read_opts(reader)?,
355
            },
356
            0x10 => CanonicalFunction::StreamWrite {
357
178
                ty: reader.read()?,
358
178
                options: read_opts(reader)?,
359
            },
360
            0x11 => CanonicalFunction::StreamCancelRead {
361
172
                ty: reader.read()?,
362
172
                async_: reader.read()?,
363
            },
364
            0x12 => CanonicalFunction::StreamCancelWrite {
365
166
                ty: reader.read()?,
366
166
                async_: reader.read()?,
367
            },
368
168
            0x13 => CanonicalFunction::StreamDropReadable { ty: reader.read()? },
369
176
            0x14 => CanonicalFunction::StreamDropWritable { ty: reader.read()? },
370
18
            0x15 => CanonicalFunction::FutureNew { ty: reader.read()? },
371
            0x16 => CanonicalFunction::FutureRead {
372
16
                ty: reader.read()?,
373
16
                options: read_opts(reader)?,
374
            },
375
            0x17 => CanonicalFunction::FutureWrite {
376
14
                ty: reader.read()?,
377
14
                options: read_opts(reader)?,
378
            },
379
            0x18 => CanonicalFunction::FutureCancelRead {
380
16
                ty: reader.read()?,
381
16
                async_: reader.read()?,
382
            },
383
            0x19 => CanonicalFunction::FutureCancelWrite {
384
14
                ty: reader.read()?,
385
14
                async_: reader.read()?,
386
            },
387
14
            0x1a => CanonicalFunction::FutureDropReadable { ty: reader.read()? },
388
16
            0x1b => CanonicalFunction::FutureDropWritable { ty: reader.read()? },
389
            0x1c => CanonicalFunction::ErrorContextNew {
390
0
                options: read_opts(reader)?,
391
            },
392
            0x1d => CanonicalFunction::ErrorContextDebugMessage {
393
0
                options: read_opts(reader)?,
394
            },
395
0
            0x1e => CanonicalFunction::ErrorContextDrop,
396
397
782
            0x1f => CanonicalFunction::WaitableSetNew,
398
            0x20 => CanonicalFunction::WaitableSetWait {
399
850
                cancellable: reader.read()?,
400
850
                memory: reader.read()?,
401
            },
402
            0x21 => CanonicalFunction::WaitableSetPoll {
403
864
                cancellable: reader.read()?,
404
864
                memory: reader.read()?,
405
            },
406
804
            0x22 => CanonicalFunction::WaitableSetDrop,
407
850
            0x23 => CanonicalFunction::WaitableJoin,
408
0
            0x26 => CanonicalFunction::ThreadIndex,
409
            0x27 => CanonicalFunction::ThreadNewIndirect {
410
0
                func_ty_index: reader.read()?,
411
0
                table_index: reader.read()?,
412
            },
413
            0x28 => CanonicalFunction::ThreadSwitchTo {
414
0
                cancellable: reader.read()?,
415
            },
416
            0x29 => CanonicalFunction::ThreadSuspend {
417
0
                cancellable: reader.read()?,
418
            },
419
0
            0x2a => CanonicalFunction::ThreadResumeLater,
420
            0x2b => CanonicalFunction::ThreadYieldTo {
421
0
                cancellable: reader.read()?,
422
            },
423
            0x06 => CanonicalFunction::SubtaskCancel {
424
852
                async_: reader.read()?,
425
            },
426
796
            0x05 => CanonicalFunction::TaskCancel,
427
            0x40 => CanonicalFunction::ThreadSpawnRef {
428
0
                func_ty_index: reader.read()?,
429
            },
430
            0x41 => CanonicalFunction::ThreadSpawnIndirect {
431
0
                func_ty_index: reader.read()?,
432
0
                table_index: reader.read()?,
433
            },
434
0
            0x42 => CanonicalFunction::ThreadAvailableParallelism,
435
0
            x => return reader.invalid_leading_byte(x, "canonical function"),
436
        })
437
19.8k
    }
438
}
439
440
6.25k
fn read_opts(reader: &mut BinaryReader<'_>) -> Result<Box<[CanonicalOption]>> {
441
6.25k
    reader
442
6.25k
        .read_iter(MAX_WASM_CANONICAL_OPTIONS, "canonical options")?
443
6.25k
        .collect::<Result<_>>()
444
6.25k
}
445
446
impl<'a> FromReader<'a> for CanonicalOption {
447
4.67k
    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
448
4.67k
        Ok(match reader.read_u8()? {
449
250
            0x00 => CanonicalOption::UTF8,
450
0
            0x01 => CanonicalOption::UTF16,
451
0
            0x02 => CanonicalOption::CompactUTF16,
452
1.35k
            0x03 => CanonicalOption::Memory(reader.read_var_u32()?),
453
244
            0x04 => CanonicalOption::Realloc(reader.read_var_u32()?),
454
1.86k
            0x05 => CanonicalOption::PostReturn(reader.read_var_u32()?),
455
672
            0x06 => CanonicalOption::Async,
456
286
            0x07 => CanonicalOption::Callback(reader.read_var_u32()?),
457
0
            0x08 => CanonicalOption::CoreType(reader.read_var_u32()?),
458
0
            0x09 => CanonicalOption::Gc,
459
0
            x => return reader.invalid_leading_byte(x, "canonical option"),
460
        })
461
4.67k
    }
462
}