Skip to main content

js_component_bindgen/intrinsics/
mod.rs

1//! Intrinsics used from JS
2
3use std::collections::{BTreeSet, HashSet};
4use std::fmt::Write;
5
6use crate::source::Source;
7use crate::{TranspileOpts, uwrite, uwriteln};
8
9pub(crate) mod conversion;
10use conversion::ConversionIntrinsic;
11
12pub(crate) mod js_helper;
13use js_helper::JsHelperIntrinsic;
14
15pub(crate) mod webidl;
16use webidl::WebIdlIntrinsic;
17
18pub(crate) mod string;
19use string::StringIntrinsic;
20
21pub(crate) mod resource;
22use resource::ResourceIntrinsic;
23
24pub(crate) mod lift;
25use lift::LiftIntrinsic;
26
27pub(crate) mod lower;
28use lower::LowerIntrinsic;
29
30pub(crate) mod component;
31use component::ComponentIntrinsic;
32
33pub(crate) mod p3;
34use p3::async_future::AsyncFutureIntrinsic;
35use p3::async_stream::AsyncStreamIntrinsic;
36use p3::async_task::AsyncTaskIntrinsic;
37use p3::error_context::ErrCtxIntrinsic;
38use p3::host::HostIntrinsic;
39use p3::waitable::WaitableIntrinsic;
40
41/// List of all intrinsics that are used by these
42///
43/// These intrinsics refer to JS code that is included in order to make
44/// transpiled WebAssembly components and their imports/exports functional
45/// in the relevant JS context.
46#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
47pub enum Intrinsic {
48    JsHelper(JsHelperIntrinsic),
49    WebIdl(WebIdlIntrinsic),
50    Conversion(ConversionIntrinsic),
51    String(StringIntrinsic),
52    Resource(ResourceIntrinsic),
53    ErrCtx(ErrCtxIntrinsic),
54    AsyncTask(AsyncTaskIntrinsic),
55    Waitable(WaitableIntrinsic),
56    Lift(LiftIntrinsic),
57    Lower(LowerIntrinsic),
58    AsyncStream(AsyncStreamIntrinsic),
59    AsyncFuture(AsyncFutureIntrinsic),
60    Component(ComponentIntrinsic),
61    Host(HostIntrinsic),
62
63    // Polyfills
64    PromiseWithResolversPonyfill,
65
66    /// Enable debug logging
67    DebugLog,
68
69    /// Global setting for determinism (used in async)
70    GlobalAsyncDeterminism,
71
72    /// Randomly produce a boolean true/false
73    CoinFlip,
74
75    // Basic type helpers
76    ConstantI32Max,
77    ConstantI32Min,
78    TypeCheckValidI32,
79    TypeCheckAsyncFn,
80    AsyncFunctionCtor,
81
82    Base64Compile,
83    ClampGuest,
84    FetchCompile,
85
86    // Globals
87    SymbolCabiDispose,
88    SymbolCabiLower,
89    SymbolResourceHandle,
90    SymbolResourceRep,
91    SymbolDispose,
92    SymbolAsyncIterator,
93    SymbolIterator,
94    ScopeId,
95    HandleTables,
96
97    /// Class that conforms to a `ReadableStreams`-like interface and is usable externally
98    ///
99    /// This is normally the `ReadableStream` class provided by the platform itself.
100    PlatformReadableStreamClass,
101
102    // Global Initializers
103    FinalizationRegistryCreate,
104
105    // Global classes
106    ComponentError,
107
108    // WASI object helpers
109    GetErrorPayload,
110    GetErrorPayloadString,
111
112    /// Class that manages (and synchronizes) writes to managed buffers
113    ManagedBufferClass,
114
115    /// Buffer manager that is used to synchronize component writes
116    BufferManagerClass,
117
118    /// Global for an instantiated buffer manager singleton
119    GlobalBufferManager,
120
121    /// Reusable table structure for holding canonical ABI objects by their representation/identifier of (e.g. resources, waitables, etc)
122    ///
123    /// Representations of objects stored in one of these tables is a u32 (0 is expected to be an invalid index).
124    RepTableClass,
125
126    /// Event codes used for async, as a JS enum
127    AsyncEventCodeEnum,
128
129    // JS helper functions
130    IsLE,
131    ThrowInvalidBool,
132    ThrowUninitialized,
133    HasOwnProperty,
134    InstantiateCore,
135
136    /// Tracking of component memories
137    GlobalComponentMemoryMap,
138
139    /// Tracking of component memories
140    RegisterGlobalMemoryForComponent,
141
142    /// Tracking of component memories
143    LookupMemoriesForComponent,
144
145    /// Global that tracks the current task
146    GlobalCurrentTaskMeta,
147
148    /// Gets the current global task state
149    GetGlobalCurrentTaskMetaFn,
150
151    /// Gets the current global task state
152    SetGlobalCurrentTaskMetaFn,
153
154    /// Execute a closure with a certain set current task
155    WithGlobalCurrentTaskMetaFn,
156
157    /// Execute an async closure with a certain set current task
158    WithGlobalCurrentTaskMetaFnAsync,
159
160    /// Clear the global task meta
161    ClearGlobalCurrentTaskMetaFn,
162}
163
164impl Intrinsic {
165    pub fn render(&self, output: &mut Source, args: &RenderIntrinsicsArgs) {
166        match self {
167            Intrinsic::JsHelper(i) => i.render(output, args),
168            Intrinsic::Conversion(i) => i.render(output, args),
169            Intrinsic::String(i) => i.render(output, args),
170            Intrinsic::ErrCtx(i) => i.render(output, args),
171            Intrinsic::Resource(i) => i.render(output, args),
172            Intrinsic::AsyncTask(i) => i.render(output, args),
173            Intrinsic::Waitable(i) => i.render(output, args),
174            Intrinsic::Lift(i) => i.render(output, args),
175            Intrinsic::Lower(i) => i.render(output, args),
176            Intrinsic::AsyncStream(i) => i.render(output, args),
177            Intrinsic::AsyncFuture(i) => i.render(output, args),
178            Intrinsic::Component(i) => i.render(output, args),
179            Intrinsic::Host(i) => i.render(output, args),
180
181            Intrinsic::GlobalAsyncDeterminism => {
182                uwriteln!(
183                    output,
184                    "const {var_name} = '{determinism}';",
185                    var_name = self.name(),
186                    determinism = args.determinism_profile,
187                );
188            }
189
190            Intrinsic::CoinFlip => {
191                uwriteln!(
192                    output,
193                    "const {var_name} = () => {{ return Math.random() > 0.5; }};",
194                    var_name = self.name(),
195                );
196            }
197
198            Intrinsic::ConstantI32Min => output.push_str(&format!(
199                "const {const_name} = -2_147_483_648;\n",
200                const_name = self.name()
201            )),
202
203            Intrinsic::ConstantI32Max => {
204                uwriteln!(
205                    output,
206                    r#"
207                      const {const_name} = 2_147_483_647;
208                    "#,
209                    const_name = self.name()
210                )
211            }
212
213            Intrinsic::TypeCheckValidI32 => {
214                let i32_const_min = Intrinsic::ConstantI32Min.name();
215                let i32_const_max = Intrinsic::ConstantI32Max.name();
216
217                uwriteln!(
218                    output,
219                    r#"
220                      const {fn_name} = (n) => typeof n === 'number' && n >= {i32_const_min} && n <= {i32_const_max};
221                    "#,
222                    fn_name = self.name()
223                );
224            }
225
226            Intrinsic::AsyncFunctionCtor => {
227                let async_fn_type = Intrinsic::AsyncFunctionCtor.name();
228                uwriteln!(
229                    output,
230                    "const {async_fn_type} = (async () => {{}}).constructor;"
231                );
232            }
233
234            Intrinsic::TypeCheckAsyncFn => {
235                let async_fn_check = Intrinsic::TypeCheckAsyncFn.name();
236                let async_fn_ctor = Intrinsic::AsyncFunctionCtor.name();
237                uwriteln!(
238                    output,
239                    r#"
240                    const {async_fn_check} = (f) => {{
241                        return f instanceof {async_fn_ctor};
242                    }};
243                    "#,
244                );
245            }
246
247            Intrinsic::Base64Compile => {
248                if !args.transpile_opts.nodejs_compat_disabled {
249                    uwriteln!(
250                        output,
251                        r#"
252                          const base64Compile = str => WebAssembly.compile(
253                              typeof Buffer !== 'undefined'
254                                  ? Buffer.from(str, 'base64')
255                                  : Uint8Array.from(atob(str), b => b.charCodeAt(0))
256                          );
257                        "#
258                    );
259                } else {
260                    uwriteln!(
261                        output,
262                        r#"
263                          const base64Compile = str => WebAssembly.compile(Uint8Array.from(atob(str), b => b.charCodeAt(0)));
264                        "#
265                    );
266                }
267            }
268
269            Intrinsic::ClampGuest => {
270                uwriteln!(
271                    output,
272                    r#"
273                      function clampGuest(i, min, max) {{
274                          if (i < min || i > max) {{
275                              throw new TypeError(`must be between ${{min}} and ${{max}}`);
276                          }}
277                          return i;
278                      }}
279                    "#
280                );
281            }
282
283            Intrinsic::ComponentError => output.push_str(
284                "
285                class ComponentError extends Error {
286                    constructor (value) {
287                        const enumerable = typeof value !== 'string';
288                        super(enumerable ? `${String(value)} (see error.payload)` : value);
289                        Object.defineProperty(this, 'payload', { value, enumerable });
290                    }
291                }
292            ",
293            ),
294
295            Intrinsic::FinalizationRegistryCreate => output.push_str(
296                "
297                function finalizationRegistryCreate (unregister) {
298                    if (typeof FinalizationRegistry === 'undefined') {
299                        return { unregister () {} };
300                    }
301                    return new FinalizationRegistry(unregister);
302                }
303            ",
304            ),
305
306            Intrinsic::FetchCompile => {
307                if !args.transpile_opts.nodejs_compat_disabled {
308                    output.push_str("
309                    const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
310                    let _fs;
311                    async function fetchCompile (url) {
312                        if (isNode) {
313                            _fs = _fs || await import('node:fs/promises');
314                            return WebAssembly.compile(await _fs.readFile(url));
315                        }
316                        return fetch(url).then(WebAssembly.compileStreaming);
317                    }
318                ")
319                } else {
320                    output.push_str(
321                        "
322                    const fetchCompile = url => fetch(url).then(WebAssembly.compileStreaming);
323                ",
324                    )
325                }
326            }
327
328            Intrinsic::GetErrorPayload => {
329                let hop = Intrinsic::HasOwnProperty.name();
330                uwrite!(
331                    output,
332                    "
333                    function getErrorPayload(e) {{
334                        if (e && {hop}.call(e, 'payload')) return e.payload;
335                        if (e instanceof Error) throw e;
336                        return e;
337                    }}
338                "
339                )
340            }
341
342            Intrinsic::GetErrorPayloadString => {
343                let hop = Intrinsic::HasOwnProperty.name();
344                uwrite!(
345                    output,
346                    "
347                    function getErrorPayloadString(e) {{
348                        if (e && {hop}.call(e, 'payload')) return e.payload;
349                        if (e instanceof Error) return e.message;
350                        return e;
351                    }}
352                "
353                )
354            }
355
356            Intrinsic::WebIdl(w) => w.render(output),
357
358            Intrinsic::HandleTables => {
359                let var_name = self.name();
360                uwriteln!(
361                    output,
362                    r#"
363                      const {var_name} = [];
364                    "#,
365                );
366            }
367
368            Intrinsic::HasOwnProperty => output.push_str(
369                "
370                const hasOwnProperty = Object.prototype.hasOwnProperty;
371            ",
372            ),
373
374            Intrinsic::InstantiateCore => {
375                if !args.instantiation_occurred {
376                    output.push_str(
377                        "
378                    const instantiateCore = WebAssembly.instantiate;
379                ",
380                    )
381                }
382            }
383
384            Intrinsic::IsLE => output.push_str(
385                "
386                const isLE = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;
387            ",
388            ),
389
390            Intrinsic::SymbolCabiDispose => output.push_str(
391                "
392                const symbolCabiDispose = Symbol.for('cabiDispose');
393            ",
394            ),
395
396            Intrinsic::SymbolCabiLower => output.push_str(
397                "
398                const symbolCabiLower = Symbol.for('cabiLower');
399            ",
400            ),
401
402            Intrinsic::ScopeId => {
403                let name = self.name();
404                uwriteln!(output, "let {name} = 0;");
405            }
406
407            Intrinsic::SymbolResourceHandle => output.push_str(
408                "
409                const symbolRscHandle = Symbol('handle');
410            ",
411            ),
412
413            Intrinsic::SymbolResourceRep => output.push_str(
414                "
415                const symbolRscRep = Symbol.for('cabiRep');
416            ",
417            ),
418
419            Intrinsic::SymbolDispose => {
420                let var_name = self.name();
421                uwriteln!(
422                    output,
423                    "const {var_name} = Symbol.dispose || Symbol.for('dispose');"
424                );
425            }
426
427            Intrinsic::SymbolAsyncIterator => {
428                let var_name = self.name();
429                uwriteln!(output, "const {var_name} = Symbol.asyncIterator;");
430            }
431
432            Intrinsic::SymbolIterator => {
433                let var_name = self.name();
434                uwriteln!(output, "const {var_name} = Symbol.iterator;");
435            }
436
437            Intrinsic::ThrowInvalidBool => output.push_str(
438                "
439                function throwInvalidBool() {
440                    throw new TypeError('invalid variant discriminant for bool');
441                }
442            ",
443            ),
444
445            Intrinsic::ThrowUninitialized => output.push_str(
446                "
447                function throwUninitialized() {
448                    throw new TypeError('Wasm uninitialized use `await $init` first');
449                }
450            ",
451            ),
452
453            Intrinsic::DebugLog => {
454                let fn_name = Intrinsic::DebugLog.name();
455                output.push_str(&format!(
456                    "
457                    const {fn_name} = (...args) => {{
458                        if (!globalThis?.process?.env?.JCO_DEBUG) {{ return; }}
459                        console.debug(...args);
460                    }};
461                "
462                ));
463            }
464
465            Intrinsic::PromiseWithResolversPonyfill => {
466                let fn_name = self.name();
467                output.push_str(&format!(
468                    r#"
469                    function {fn_name}() {{
470                        if (Promise.withResolvers) {{
471                            return Promise.withResolvers();
472                        }} else {{
473                            let resolve;
474                            let reject;
475                            const promise = new Promise((res, rej) => {{
476                                resolve = res;
477                                reject = rej;
478                            }});
479                            return {{ promise, resolve, reject }};
480                        }}
481                    }}
482                "#
483                ));
484            }
485
486            Intrinsic::AsyncEventCodeEnum => {
487                let name = Intrinsic::AsyncEventCodeEnum.name();
488                output.push_str(&format!(
489                    "
490                    const {name} = {{
491                        NONE: 0,
492                        SUBTASK: 1,
493                        STREAM_READ: 2,
494                        STREAM_WRITE: 3,
495                        FUTURE_READ: 4,
496                        FUTURE_WRITE: 5,
497                        TASK_CANCELLED: 6,
498                    }};
499                "
500                ));
501            }
502
503            Intrinsic::ManagedBufferClass => {
504                let debug_log_fn = Intrinsic::DebugLog.name();
505                let managed_buffer_class = Intrinsic::ManagedBufferClass.name();
506                output.push_str(&format!(
507                    r#"
508                    class {managed_buffer_class} {{
509                        static MAX_LENGTH = 2**28 - 1;
510                        #componentIdx;
511                        #memory;
512
513                        #elemMeta = null;
514
515                        #start;
516                        #ptr;
517                        capacity;
518                        processed = 0;
519
520                        #hostOnlyData; // initial data (only filled out for host-owned)
521
522                        target;
523
524                        constructor(args) {{
525                            if (args.capacity > {managed_buffer_class}.MAX_LENGTH) {{
526                                 throw new Error(`buffer size [${{args.capacity}}] greater than max length`);
527                            }}
528                            if (args.componentIdx === undefined) {{ throw new TypeError('missing/invalid component idx'); }}
529                            if (args.capacity === undefined) {{ throw new TypeError('missing/invalid capacity'); }}
530                            if (!args.elemMeta || typeof args.elemMeta.align32 !== 'number') {{
531                                throw new TypeError('missing/invalid element metadata');
532                            }}
533
534                            if (!args.memory && args.start === undefined && args.data === undefined) {{
535                                throw new TypeError('either memory and start ptr or data must be provided for managed buffers');
536                            }}
537
538                            if (args.memory && args.start == undefined) {{
539                                throw new TypeError('missing/invalid start ptr, depsite memory being present');
540                            }}
541
542                            if (!args.elemMeta.isNone && args.capacity > 0) {{
543                                if (args.start && args.start % args.elemMeta.align32 !== 0) {{
544                                    throw new Error(`invalid alignment: type with 32bit alignment [${{args.elemMeta.align32}}] at starting pointer [${{args.start}}]`);
545                                }}
546                                // TODO: memory lenght bounds check
547                            }}
548
549                            this.#componentIdx = args.componentIdx;
550                            this.#memory = args.memory;
551                            this.#start = args.start;
552                            this.#ptr = this.#start;
553                            this.capacity = args.capacity;
554                            this.#elemMeta = args.elemMeta;
555
556                            if (args.data !== undefined && !Array.isArray(args.data)) {{
557                                throw new TypeError('host-only data must be an array');
558                            }}
559                            this.#hostOnlyData = args.data;
560
561                            this.target = args.target;
562                        }}
563
564                        setTarget(tgt) {{ this.target = tgt; }}
565
566                        remaining() {{
567                            return this.capacity - this.processed;
568                        }}
569
570                        componentIdx() {{ return this.#componentIdx; }}
571
572                        getElemMeta() {{ return this.#elemMeta; }}
573
574                        isHostOwned() {{ return !this.#memory; }}
575
576                        read(count) {{
577                            {debug_log_fn}('[{managed_buffer_class}#read()] args', {{ count }});
578                            if (count === undefined || count <= 0) {{
579                                throw new TypeError(`missing/invalid count [${{count}}]`);
580                            }}
581
582                            const cap = this.capacity;
583                            if (count > cap) {{
584                                throw new Error(`cannot read [${{count}}] elements from buffer with capacity [${{cap}}]`);
585                            }}
586
587                            let values = [];
588                            if (this.#elemMeta.isNone) {{
589                                values = [...new Array(count)].map(() => null);
590                            }} else {{
591                                if (this.isHostOwned()) {{
592                                    values = this.#hostOnlyData.slice(0, count);
593                                    this.#hostOnlyData = this.#hostOnlyData.slice(count);
594                                }} else if (this.#elemMeta.payloadTypeName === 'U8') {{
595                                    values = Array.from(new Uint8Array(this.#memory.buffer, this.#ptr, count));
596                                    this.#ptr += count;
597                                }} else {{
598                                    let currentCount = count;
599                                    let startPtr = this.#ptr;
600                                    if (this.#elemMeta.stringEncoding === undefined) {{
601                                        throw new Error('string encoding unknown during read');
602                                    }}
603                                    let liftCtx = {{
604                                        storagePtr: startPtr,
605                                        memory: this.#memory,
606                                        componentIdx: this.#componentIdx,
607                                        stringEncoding: this.#elemMeta.stringEncoding,
608                                    }};
609                                    if (currentCount < 0) {{ throw new Error('unexpectedly invalid count'); }}
610                                    while (currentCount > 0) {{
611                                        const [value, _ctx] = this.#elemMeta.liftFn(liftCtx);
612                                        values.push(value);
613                                        currentCount -= 1;
614                                    }}
615                                    this.#ptr = liftCtx.storagePtr;
616                                }}
617                            }}
618
619                            this.processed += count;
620                            return values;
621                        }}
622
623                        write(values) {{
624                            {debug_log_fn}('[{managed_buffer_class}#write()] args', {{ values }});
625
626                            if (!Array.isArray(values)) {{ throw new TypeError('values input to write() must be an array'); }}
627                            let rc = this.remaining();
628                            if (values.length > rc) {{
629                                throw new Error(`cannot write [${{values.length}}] elements to managed buffer with remaining capacity [${{rc}}]`);
630                            }}
631
632                            if (this.#elemMeta.isNone) {{
633                                if (!values.every(v => v === null)) {{
634                                    throw new Error('non-null values in write() to unit managed buffer');
635                                }}
636                            }} else {{
637                                if (this.isHostOwned()) {{
638                                    this.#hostOnlyData = this.#hostOnlyData.concat(values);
639                                }} else if (this.#elemMeta.payloadTypeName === 'U8') {{
640                                    new Uint8Array(this.#memory.buffer, this.#ptr, values.length).set(values);
641                                    this.#ptr += values.length;
642                                }} else {{
643                                    let startPtr = this.#ptr;
644                                    if (this.#elemMeta.stringEncoding === undefined) {{
645                                        throw new Error('string encoding unknown during write');
646                                    }}
647
648                                    const lowerCtx = {{
649                                        memory: this.#memory,
650                                        storagePtr: startPtr,
651                                        componentIdx: this.#componentIdx,
652                                        stringEncoding: this.#elemMeta.stringEncoding,
653                                        realloc: this.#elemMeta.getReallocFn?.(),
654                                        getReallocFn: this.#elemMeta.getReallocFn,
655                                    }}
656                                    for (const v of values) {{
657                                        lowerCtx.vals = [v];
658                                        this.#elemMeta.lowerFn(lowerCtx);
659                                    }}
660
661                                    this.#ptr = lowerCtx.storagePtr;
662                                }}
663                            }}
664
665                            this.processed += values.length;
666                        }}
667
668                    }}
669                "#
670                ));
671            }
672
673            Intrinsic::BufferManagerClass => {
674                let debug_log_fn = Intrinsic::DebugLog.name();
675                let buffer_manager_class = Intrinsic::BufferManagerClass.name();
676                let managed_buffer_class = Intrinsic::ManagedBufferClass.name();
677
678                output.push_str(&format!(r#"
679                    class {buffer_manager_class} {{
680                        #buffers = new Map();
681                        #bufferIDs = new Map();
682
683                        // NOTE: componentIdx === -1 indicates the host
684                        getNextBufferID(componentIdx) {{
685                            const current = this.#bufferIDs.get(componentIdx);
686                            if (current === undefined) {{
687                                this.#bufferIDs.set(componentIdx, 1n);
688                                return 1n;
689                            }}
690                            const next = current + 1n;
691                            this.#bufferIDs.set(componentIdx, next);
692                            return next;
693                        }}
694
695                        getBuffer(componentIdx, bufferID) {{
696                            {debug_log_fn}('[{buffer_manager_class}#getBuffer()] args', {{ componentIdx, bufferID }});
697                            return this.#buffers.get(componentIdx)?.get(bufferID);
698                        }}
699
700                        createBuffer(args) {{
701                            {debug_log_fn}('[{buffer_manager_class}#createBuffer()] args', args);
702                            if (!args || typeof args !== 'object') {{ throw new TypeError('missing/invalid argument object'); }}
703
704                            if (args.start === undefined && args.data === undefined) {{
705                                throw new  TypeError('either a starting pointer or initial values must be provided');
706                            }}
707
708                            if (args.start !== undefined && args.componentIdx === undefined) {{ throw new TypeError('missing/invalid component idx'); }}
709                            if (args.count === undefined) {{ throw new TypeError('missing/invalid obj count'); }}
710                            if (!args.elemMeta) {{ throw new TypeError('missing/invalid element metadata for use with managed buffer'); }}
711
712                            const {{ componentIdx, data, start, count }} = args;
713
714                            if (!this.#buffers.has(componentIdx)) {{ this.#buffers.set(componentIdx, new Map()); }}
715                            const instanceBuffers = this.#buffers.get(componentIdx);
716
717                            const nextBufID = this.getNextBufferID(componentIdx);
718
719                            const buffer = new {managed_buffer_class}({{
720                                componentIdx,
721                                memory: args.memory,
722                                start: args.start,
723                                capacity: args.count,
724                                elemMeta: args.elemMeta,
725                                data: args.data,
726                                target: args.target,
727                                stringEncoding: args.stringEncoding,
728                            }});
729
730                            if (instanceBuffers.has(nextBufID)) {{
731                                throw new Error(`managed buffer with ID [${{nextBufID}}] already exists`);
732                            }}
733                            instanceBuffers.set(nextBufID, buffer);
734
735                            return {{ id: nextBufID, buffer }};
736                        }}
737
738                        deleteBuffer(componentIdx, bufferID) {{
739                            {debug_log_fn}('[{buffer_manager_class}#deleteBuffer()] args', {{ componentIdx, bufferID }});
740                            return this.#buffers.get(componentIdx)?.delete(bufferID);
741                        }}
742
743                    }}
744                "#));
745            }
746
747            Intrinsic::GlobalBufferManager => {
748                let global_buffer_manager = Intrinsic::GlobalBufferManager.name();
749                let buffer_manager_class = Intrinsic::BufferManagerClass.name();
750                output.push_str(&format!(
751                    "const {global_buffer_manager} = new {buffer_manager_class}();"
752                ));
753            }
754
755            Intrinsic::RepTableClass => {
756                let debug_log_fn = Intrinsic::DebugLog.name();
757                let rep_table_class = Intrinsic::RepTableClass.name();
758                output.push_str(&format!(r#"
759                    class {rep_table_class} {{
760                        #data = [0, null];
761                        #target;
762
763                        constructor(args) {{
764                            this.target = args?.target;
765                        }}
766
767                        data() {{ return this.#data; }}
768
769                        insert(val) {{
770                            {debug_log_fn}('[{rep_table_class}#insert()] args', {{ val, target: this.target }});
771                            const freeIdx = this.#data[0];
772                            if (freeIdx === 0) {{
773                                this.#data.push(val);
774                                this.#data.push(null);
775                                const rep = (this.#data.length >> 1) - 1;
776                                {debug_log_fn}('[{rep_table_class}#insert()] inserted', {{ val, target: this.target, rep }});
777                                return rep;
778                            }}
779                            this.#data[0] = this.#data[freeIdx << 1];
780                            const placementIdx = freeIdx << 1;
781                            this.#data[placementIdx] = val;
782                            this.#data[placementIdx + 1] = null;
783                            {debug_log_fn}('[{rep_table_class}#insert()] inserted', {{ val, target: this.target, rep: freeIdx }});
784                            return freeIdx;
785                        }}
786
787                        get(rep) {{
788                            {debug_log_fn}('[{rep_table_class}#get()] args', {{ rep, target: this.target }});
789                            if (rep === 0) {{ throw new Error('invalid resource rep during get, (cannot be 0)'); }}
790
791                            const baseIdx = rep << 1;
792                            const val = this.#data[baseIdx];
793                            return val;
794                        }}
795
796                        contains(rep) {{
797                            {debug_log_fn}('[{rep_table_class}#contains()] args', {{ rep, target: this.target }});
798                            if (rep === 0) {{ throw new Error('invalid resource rep during contains, (cannot be 0)'); }}
799
800                            const baseIdx = rep << 1;
801                            return !!this.#data[baseIdx];
802                        }}
803
804                        remove(rep) {{
805                            {debug_log_fn}('[{rep_table_class}#remove()] args', {{ rep, target: this.target }});
806                            if (rep === 0) {{ throw new Error('invalid resource rep during remove, (cannot be 0)'); }}
807                            if (this.#data.length === 2) {{ throw new Error('invalid'); }}
808
809                            const baseIdx = rep << 1;
810                            const val = this.#data[baseIdx];
811
812                            this.#data[baseIdx] = this.#data[0];
813                            this.#data[0] = rep;
814
815                            return val;
816                        }}
817
818                        clear() {{
819                            {debug_log_fn}('[{rep_table_class}#clear()] args', {{ rep, target: this.target }});
820                            this.#data = [0, null];
821                        }}
822                    }}
823                "#));
824            }
825
826            Intrinsic::GlobalComponentMemoryMap => {
827                let global_component_memory_map = Intrinsic::GlobalComponentMemoryMap.name();
828                output.push_str(&format!(
829                    "const {global_component_memory_map} = new Map();\n"
830                ));
831            }
832
833            Intrinsic::RegisterGlobalMemoryForComponent => {
834                let global_component_memory_map = Intrinsic::GlobalComponentMemoryMap.name();
835                let register_global_component_memory =
836                    Intrinsic::RegisterGlobalMemoryForComponent.name();
837                output.push_str(&format!(
838                    r#"
839                      function {register_global_component_memory}(args) {{
840                          const {{ componentIdx, memory, memoryIdx }} = args ?? {{}};
841                          if (componentIdx === undefined) {{ throw new TypeError('missing component idx'); }}
842                          if (memory === undefined && memoryIdx === undefined) {{ throw new TypeError('missing both memory & memory idx'); }}
843                          let inner = {global_component_memory_map}.get(componentIdx);
844                          if (!inner) {{
845                              inner = {{}};
846                              {global_component_memory_map}.set(componentIdx, inner);
847                          }}
848
849                          inner[memoryIdx] = {{ memory, memoryIdx, componentIdx }};
850                      }}
851                    "#)
852                );
853            }
854
855            Intrinsic::LookupMemoriesForComponent => {
856                let global_component_memory_map = Intrinsic::GlobalComponentMemoryMap.name();
857                let lookup_global_memories_for_component =
858                    Intrinsic::LookupMemoriesForComponent.name();
859                output.push_str(&format!(
860                    r#"
861                      function {lookup_global_memories_for_component}(args) {{
862                          const {{ componentIdx }} = args ?? {{}};
863                          if (args.componentIdx === undefined) {{ throw new TypeError("missing component idx"); }}
864
865                          const metas = {global_component_memory_map}.get(componentIdx);
866                          if (!metas) {{ return []; }}
867
868                          if (args.memoryIdx === undefined) {{
869                              return Object.values(metas);
870                          }}
871
872                          const meta = metas[args.memoryIdx];
873                          return meta?.memory;
874                      }}
875                    "#)
876                );
877            }
878
879            Self::GlobalCurrentTaskMeta => {
880                let name = self.name();
881                output.push_str(&format!("const {name} = {{}};\n"));
882            }
883
884            Self::GetGlobalCurrentTaskMetaFn => {
885                let get_current_global_task_meta_fn = Self::GetGlobalCurrentTaskMetaFn.name();
886                let global_current_task_meta_obj = Self::GlobalCurrentTaskMeta.name();
887
888                uwriteln!(
889                    output,
890                    r#"
891                      function {get_current_global_task_meta_fn}(componentIdx) {{
892                          const v = {global_current_task_meta_obj}[componentIdx];
893                          if (v === undefined || v === null) {{ return undefined; }}
894                          return {{ ...v }};
895                      }}
896                    "#,
897                );
898            }
899
900            Self::SetGlobalCurrentTaskMetaFn => {
901                let set_global_current_task_meta_fn = self.name();
902                let global_current_task_meta_obj = Self::GlobalCurrentTaskMeta.name();
903
904                uwriteln!(
905                    output,
906                    r#"
907                      function {set_global_current_task_meta_fn}(args) {{
908                          if (!args) {{ throw new TypeError('args missing'); }}
909                          if (args.taskID === undefined) {{ throw new TypeError('missing task ID'); }}
910                          if (args.componentIdx === undefined) {{ throw new TypeError('missing component idx'); }}
911                          const {{ taskID, componentIdx }} = args;
912                          return {global_current_task_meta_obj}[componentIdx] = {{ taskID, componentIdx }};
913                      }}
914                    "#,
915                );
916            }
917
918            Self::WithGlobalCurrentTaskMetaFn => {
919                let debug_log_fn = Intrinsic::DebugLog.name();
920                let with_global_current_task_meta_fn = Self::WithGlobalCurrentTaskMetaFn.name();
921                let global_current_task_meta_obj = Self::GlobalCurrentTaskMeta.name();
922
923                output.push_str(&format!(
924                    r#"
925                      function {with_global_current_task_meta_fn}(args) {{
926                          {debug_log_fn}('[{with_global_current_task_meta_fn}()] args', args);
927                          if (!args) {{ throw new TypeError('args missing'); }}
928                          if (args.taskID === undefined) {{ throw new TypeError('missing task ID'); }}
929                          if (args.componentIdx === undefined) {{ throw new TypeError('missing component idx'); }}
930                          if (!args.fn) {{ throw new TypeError('missing fn'); }}
931                          const {{ taskID, componentIdx, fn }} = args;
932
933                          try {{
934                              {global_current_task_meta_obj}[componentIdx] = {{ taskID, componentIdx }};
935                              return fn();
936                          }} catch (err) {{
937                              {debug_log_fn}("error while executing sync callee/callback", {{
938                                  ...args,
939                                  err,
940                              }});
941                              throw err;
942                          }} finally {{
943                              {global_current_task_meta_obj}[componentIdx] = null;
944                          }}
945                      }}
946                    "#,
947                ));
948            }
949
950            Self::WithGlobalCurrentTaskMetaFnAsync => {
951                let debug_log_fn = Intrinsic::DebugLog.name();
952                let with_global_current_task_meta_async_fn =
953                    Self::WithGlobalCurrentTaskMetaFnAsync.name();
954                let global_current_task_meta_obj = Self::GlobalCurrentTaskMeta.name();
955                let get_or_create_async_state_fn = ComponentIntrinsic::GetOrCreateAsyncState.name();
956
957                output.push_str(&format!(
958                    r#"
959                      async function {with_global_current_task_meta_async_fn}(args) {{
960                          {debug_log_fn}('[{with_global_current_task_meta_async_fn}()] args', args);
961                          if (!args) {{ throw new TypeError('args missing'); }}
962                          if (args.taskID === undefined) {{ throw new TypeError('missing task ID'); }}
963                          if (args.componentIdx === undefined) {{ throw new TypeError('missing component idx'); }}
964                          if (!args.fn) {{ throw new TypeError('missing fn'); }}
965                          const {{ taskID, componentIdx, fn }} = args;
966
967                          // If there is already an async task executing, we must wait for it
968                          // to complete before we can can run the closure we were given
969                          //
970                          let current = {global_current_task_meta_obj}[componentIdx];
971                          let cstate;
972                          if (current && current.taskID !== taskID) {{
973                              cstate = {get_or_create_async_state_fn}(componentIdx);
974                              while (current && current.taskID !== taskID) {{
975                                  const {{ promise, resolve }} = Promise.withResolvers();
976                                  cstate.onNextExclusiveRelease(resolve);
977                                  await promise;
978                                  current = {global_current_task_meta_obj}[componentIdx];
979                              }}
980
981                              // Since we've just waited for the component to not be locked, re-lock
982                              // exclusivity so we can run the fn below (likely a callee/callback)
983                              cstate.exclusiveLock();
984                          }}
985
986                          try {{
987                              {global_current_task_meta_obj}[componentIdx] = {{ taskID, componentIdx }};
988                              return await fn();
989                          }} catch (err) {{
990                              {debug_log_fn}("error while executing async callee/callback", {{
991                                  ...args,
992                                  err,
993                              }});
994                              throw err;
995                          }} finally {{
996                              {global_current_task_meta_obj}[componentIdx] = null;
997                          }}
998                      }}
999                    "#,
1000                ));
1001            }
1002
1003            Self::ClearGlobalCurrentTaskMetaFn => {
1004                let debug_log_fn = Intrinsic::DebugLog.name();
1005                let clear_global_current_task_meta_fn = Self::ClearGlobalCurrentTaskMetaFn.name();
1006                let global_current_task_meta_obj = Self::GlobalCurrentTaskMeta.name();
1007
1008                output.push_str(&format!(
1009                    r#"
1010                      async function {clear_global_current_task_meta_fn}(args) {{
1011                          {debug_log_fn}('[{clear_global_current_task_meta_fn}()] args', args);
1012                          if (!args) {{ throw new TypeError('args missing'); }}
1013                          if (args.taskID === undefined) {{ throw new TypeError('missing task ID'); }}
1014                          if (args.componentIdx === undefined) {{ throw new TypeError('missing component idx'); }}
1015                          const {{ taskID, componentIdx }} = args;
1016
1017                          const meta = {global_current_task_meta_obj}[componentIdx];
1018                          if (!meta) {{ throw new Error(`missing current task meta for component idx [${{componentIdx}}]n`); }}
1019
1020                          if (meta.taskID !== taskID) {{
1021                              throw new Error(`task ID [${{meta.taskID}}] != requested ID [${{taskID}}]`);
1022                          }}
1023                          if (meta.componentIdx !== componentIdx) {{
1024                              throw new Error(`component idx [${{meta.componentIdx}}] != requested idx [${{componentIdx}}]`);
1025                          }}
1026
1027                          {global_current_task_meta_obj}[componentIdx] = null;
1028                      }}
1029                    "#,
1030                ));
1031            }
1032
1033            // TODO(feat): customizable stream classes
1034            Intrinsic::PlatformReadableStreamClass => {
1035                let name = self.name();
1036                uwriteln!(
1037                    output,
1038                    r#"
1039                        if (!ReadableStream) {{
1040                            throw new Error('builtin stream class [ReadableStream] is not available');
1041                        }}
1042                        const {name} = ReadableStream;
1043                    "#
1044                );
1045            }
1046        }
1047    }
1048}
1049
1050/// Profile for determinism to be used by async implementation
1051#[derive(Debug, Default, PartialEq, Eq)]
1052pub enum AsyncDeterminismProfile {
1053    /// Allow random ordering non-determinism
1054    #[default]
1055    Random,
1056
1057    /// Require determinism
1058    #[allow(unused)]
1059    Deterministic,
1060}
1061
1062impl std::fmt::Display for AsyncDeterminismProfile {
1063    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1064        write!(
1065            f,
1066            "{}",
1067            match self {
1068                Self::Deterministic => "deterministic",
1069                Self::Random => "random",
1070            }
1071        )
1072    }
1073}
1074
1075/// Arguments to `render_intrinsics`
1076#[derive(bon::Builder)]
1077#[non_exhaustive]
1078pub struct RenderIntrinsicsArgs<'a> {
1079    /// List of intrinsics being built for use
1080    pub(crate) intrinsics: &'a mut BTreeSet<Intrinsic>,
1081    /// Whether instantiation has occurred
1082    #[builder(default)]
1083    pub(crate) instantiation_occurred: bool,
1084    /// The kind of determinism to use
1085    #[builder(default)]
1086    pub(crate) determinism_profile: AsyncDeterminismProfile,
1087    /// Options provided when performing transpilation
1088    pub(crate) transpile_opts: &'a TranspileOpts,
1089}
1090
1091/// Intrinsics that should be rendered as early as possible
1092const EARLY_INTRINSICS: [Intrinsic; 37] = [
1093    Intrinsic::PromiseWithResolversPonyfill,
1094    Intrinsic::SymbolDispose,
1095    Intrinsic::SymbolAsyncIterator,
1096    Intrinsic::SymbolIterator,
1097    Intrinsic::DebugLog,
1098    Intrinsic::GlobalAsyncDeterminism,
1099    Intrinsic::GlobalComponentMemoryMap,
1100    Intrinsic::GlobalCurrentTaskMeta,
1101    Intrinsic::GetGlobalCurrentTaskMetaFn,
1102    Intrinsic::SetGlobalCurrentTaskMetaFn,
1103    Intrinsic::WithGlobalCurrentTaskMetaFn,
1104    Intrinsic::WithGlobalCurrentTaskMetaFnAsync,
1105    Intrinsic::ClearGlobalCurrentTaskMetaFn,
1106    Intrinsic::LookupMemoriesForComponent,
1107    Intrinsic::RegisterGlobalMemoryForComponent,
1108    Intrinsic::RepTableClass,
1109    Intrinsic::CoinFlip,
1110    Intrinsic::ScopeId,
1111    // Type checking helpers
1112    Intrinsic::ConstantI32Min,
1113    Intrinsic::ConstantI32Max,
1114    Intrinsic::Conversion(ConversionIntrinsic::IsValidNumericPrimitive),
1115    Intrinsic::Conversion(ConversionIntrinsic::RequireValidNumericPrimitive),
1116    Intrinsic::TypeCheckValidI32,
1117    Intrinsic::TypeCheckAsyncFn,
1118    // Resources
1119    Intrinsic::Resource(ResourceIntrinsic::ResourceCallBorrows),
1120    // Async helpers
1121    Intrinsic::AsyncFunctionCtor,
1122    Intrinsic::AsyncTask(AsyncTaskIntrinsic::ClearCurrentTask),
1123    Intrinsic::AsyncTask(AsyncTaskIntrinsic::CurrentTaskMayBlock),
1124    Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentTaskIds),
1125    Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentComponentIdxs),
1126    Intrinsic::AsyncTask(AsyncTaskIntrinsic::UnpackCallbackResult),
1127    Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncSubtaskClass),
1128    // Host helpers
1129    Intrinsic::Host(HostIntrinsic::PrepareCall),
1130    Intrinsic::Host(HostIntrinsic::AsyncStartCall),
1131    Intrinsic::Host(HostIntrinsic::SyncStartCall),
1132    // Waitable helpers
1133    Intrinsic::Waitable(WaitableIntrinsic::WaitableClass),
1134    // Error context helpers
1135    Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalErrCtxTableMap),
1136];
1137
1138/// Emits the intrinsic `i` to this file and then returns the name of the
1139/// intrinsic.
1140pub fn render_intrinsics(args: RenderIntrinsicsArgs) -> Source {
1141    let mut output = Source::default();
1142    let mut rendered_intrinsics = HashSet::new();
1143
1144    // Render some early intrinsics
1145    for intrinsic in EARLY_INTRINSICS {
1146        intrinsic.render(&mut output, &args);
1147        rendered_intrinsics.insert(intrinsic.name());
1148    }
1149
1150    // Add intrinsics to the list we must render
1151    if args.intrinsics.contains(&Intrinsic::GetErrorPayload)
1152        || args.intrinsics.contains(&Intrinsic::GetErrorPayloadString)
1153    {
1154        args.intrinsics.insert(Intrinsic::HasOwnProperty);
1155    }
1156    if args
1157        .intrinsics
1158        .contains(&Intrinsic::String(StringIntrinsic::Utf16Encode))
1159    {
1160        args.intrinsics.insert(Intrinsic::IsLE);
1161    }
1162
1163    if args
1164        .intrinsics
1165        .contains(&Intrinsic::Conversion(ConversionIntrinsic::F32ToI32))
1166        || args
1167            .intrinsics
1168            .contains(&Intrinsic::Conversion(ConversionIntrinsic::I32ToF32))
1169    {
1170        output.push_str(
1171            "
1172            const i32ToF32I = new Int32Array(1);
1173            const i32ToF32F = new Float32Array(i32ToF32I.buffer);
1174        ",
1175        );
1176    }
1177
1178    if args
1179        .intrinsics
1180        .contains(&Intrinsic::Conversion(ConversionIntrinsic::F64ToI64))
1181        || args
1182            .intrinsics
1183            .contains(&Intrinsic::Conversion(ConversionIntrinsic::I64ToF64))
1184    {
1185        output.push_str(
1186            "
1187            const i64ToF64I = new BigInt64Array(1);
1188            const i64ToF64F = new Float64Array(i64ToF64I.buffer);
1189        ",
1190        );
1191    }
1192
1193    if args.intrinsics.contains(&Intrinsic::Resource(
1194        ResourceIntrinsic::ResourceTransferBorrow,
1195    )) || args.intrinsics.contains(&Intrinsic::Resource(
1196        ResourceIntrinsic::ResourceTransferBorrowValidLifting,
1197    )) {
1198        args.intrinsics.insert(Intrinsic::Resource(
1199            ResourceIntrinsic::ResourceTableCreateBorrow,
1200        ));
1201    }
1202
1203    if args
1204        .intrinsics
1205        .contains(&Intrinsic::String(StringIntrinsic::Utf8Encode))
1206        || args
1207            .intrinsics
1208            .contains(&Intrinsic::String(StringIntrinsic::Utf8EncodeAsync))
1209    {
1210        args.intrinsics.extend([
1211            &Intrinsic::IsLE,
1212            &Intrinsic::String(StringIntrinsic::GlobalTextEncoderUtf8),
1213        ]);
1214    }
1215
1216    if args
1217        .intrinsics
1218        .contains(&Intrinsic::String(StringIntrinsic::Utf16Encode))
1219        || args
1220            .intrinsics
1221            .contains(&Intrinsic::String(StringIntrinsic::Utf16EncodeAsync))
1222    {
1223        args.intrinsics.extend([&Intrinsic::IsLE]);
1224    }
1225
1226    // Attempting to perform a debug message hoist will require string encoding to memory
1227    if args.intrinsics.contains(&Intrinsic::ErrCtx(
1228        ErrCtxIntrinsic::ErrorContextDebugMessage,
1229    )) {
1230        args.intrinsics.extend([
1231            &Intrinsic::String(StringIntrinsic::Utf8Encode),
1232            &Intrinsic::String(StringIntrinsic::Utf16Encode),
1233            &Intrinsic::ErrCtx(ErrCtxIntrinsic::GetLocalTable),
1234        ]);
1235    }
1236
1237    if args
1238        .intrinsics
1239        .contains(&Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextNew))
1240    {
1241        args.intrinsics.extend([
1242            &Intrinsic::ErrCtx(ErrCtxIntrinsic::ComponentGlobalTable),
1243            &Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalRefCountAdd),
1244            &Intrinsic::ErrCtx(ErrCtxIntrinsic::ReserveGlobalRep),
1245            &Intrinsic::ErrCtx(ErrCtxIntrinsic::CreateLocalHandle),
1246            &Intrinsic::ErrCtx(ErrCtxIntrinsic::GetLocalTable),
1247        ]);
1248    }
1249
1250    if args.intrinsics.contains(&Intrinsic::ErrCtx(
1251        ErrCtxIntrinsic::ErrorContextDebugMessage,
1252    )) {
1253        args.intrinsics.extend([
1254            &Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalRefCountAdd),
1255            &Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextDrop),
1256            &Intrinsic::ErrCtx(ErrCtxIntrinsic::GetLocalTable),
1257        ]);
1258    }
1259
1260    if args
1261        .intrinsics
1262        .contains(&Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextGet))
1263        || args
1264            .intrinsics
1265            .contains(&Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextSet))
1266    {
1267        args.intrinsics.extend([
1268            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentTaskMap),
1269            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncTaskClass),
1270            &Intrinsic::AsyncEventCodeEnum,
1271            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask),
1272        ]);
1273    }
1274
1275    if args
1276        .intrinsics
1277        .contains(&Intrinsic::AsyncTask(AsyncTaskIntrinsic::DriverLoop))
1278    {
1279        args.intrinsics.extend([
1280            &Intrinsic::TypeCheckValidI32,
1281            &Intrinsic::Conversion(ConversionIntrinsic::ToInt32),
1282            &Intrinsic::Component(ComponentIntrinsic::ComponentStateSetAllError),
1283        ]);
1284    }
1285
1286    if args.intrinsics.contains(&Intrinsic::Component(
1287        ComponentIntrinsic::GetOrCreateAsyncState,
1288    )) {
1289        args.intrinsics.extend([&Intrinsic::RepTableClass]);
1290    }
1291
1292    if args
1293        .intrinsics
1294        .contains(&Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncTaskClass))
1295    {
1296        args.intrinsics.extend([
1297            &Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState),
1298            &Intrinsic::Component(ComponentIntrinsic::GlobalAsyncStateMap),
1299            &Intrinsic::RepTableClass,
1300            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncSubtaskClass),
1301            &Intrinsic::Waitable(WaitableIntrinsic::WaitableClass),
1302        ]);
1303    }
1304
1305    if args
1306        .intrinsics
1307        .contains(&Intrinsic::Waitable(WaitableIntrinsic::WaitableSetNew))
1308    {
1309        args.intrinsics
1310            .extend([&Intrinsic::Waitable(WaitableIntrinsic::WaitableSetClass)]);
1311    }
1312
1313    if args
1314        .intrinsics
1315        .contains(&Intrinsic::Waitable(WaitableIntrinsic::WaitableSetPoll))
1316        || args
1317            .intrinsics
1318            .contains(&Intrinsic::Waitable(WaitableIntrinsic::WaitableSetWait))
1319    {
1320        args.intrinsics
1321            .extend([&Intrinsic::Host(HostIntrinsic::StoreEventInComponentMemory)]);
1322    }
1323
1324    if args
1325        .intrinsics
1326        .contains(&Intrinsic::Waitable(WaitableIntrinsic::WaitableSetDrop))
1327    {
1328        args.intrinsics
1329            .extend([&Intrinsic::Waitable(WaitableIntrinsic::RemoveWaitableSet)]);
1330    }
1331
1332    if args.intrinsics.contains(&Intrinsic::Component(
1333        ComponentIntrinsic::GetOrCreateAsyncState,
1334    )) {
1335        args.intrinsics.extend([
1336            &Intrinsic::Component(ComponentIntrinsic::ComponentAsyncStateClass),
1337            &Intrinsic::Component(ComponentIntrinsic::GlobalAsyncStateMap),
1338        ]);
1339    }
1340
1341    if args.intrinsics.contains(&Intrinsic::Component(
1342        ComponentIntrinsic::ComponentAsyncStateClass,
1343    )) {
1344        args.intrinsics.extend([&Intrinsic::AsyncStream(
1345            AsyncStreamIntrinsic::GlobalStreamMap,
1346        )]);
1347    }
1348
1349    if args
1350        .intrinsics
1351        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatResult))
1352        | args
1353            .intrinsics
1354            .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatOption))
1355        | args
1356            .intrinsics
1357            .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatOption))
1358    {
1359        args.intrinsics
1360            .extend([&Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant)]);
1361    }
1362
1363    if args
1364        .intrinsics
1365        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant))
1366    {
1367        args.intrinsics.extend([
1368            &Intrinsic::Lift(LiftIntrinsic::LiftFlatU8),
1369            &Intrinsic::Lift(LiftIntrinsic::LiftFlatU16),
1370            &Intrinsic::Lift(LiftIntrinsic::LiftFlatU32),
1371        ]);
1372    }
1373
1374    if args
1375        .intrinsics
1376        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatResult))
1377    {
1378        args.intrinsics
1379            .insert(Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant));
1380    }
1381
1382    if args
1383        .intrinsics
1384        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatOption))
1385    {
1386        args.intrinsics
1387            .insert(Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant));
1388    }
1389
1390    if args
1391        .intrinsics
1392        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant))
1393    {
1394        args.intrinsics.extend([
1395            &Intrinsic::Lower(LowerIntrinsic::LowerFlatU8),
1396            &Intrinsic::Lower(LowerIntrinsic::LowerFlatU16),
1397            &Intrinsic::Lower(LowerIntrinsic::LowerFlatU32),
1398        ]);
1399    }
1400
1401    if args
1402        .intrinsics
1403        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatStream))
1404    {
1405        args.intrinsics.extend([
1406            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamMap),
1407            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::ExternalStreamClass),
1408            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::InternalStreamClass),
1409            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::IsStreamLowerableObject),
1410            &Intrinsic::SymbolResourceRep,
1411            &Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState),
1412            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GenReadFnFromLowerableStream),
1413            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GenStreamHostInjectFn),
1414            &Intrinsic::Lower(LowerIntrinsic::LowerFlatU32),
1415        ])
1416    }
1417
1418    if args.intrinsics.contains(&Intrinsic::AsyncStream(
1419        AsyncStreamIntrinsic::GenStreamHostInjectFn,
1420    )) {
1421        args.intrinsics.insert(Intrinsic::AsyncStream(
1422            AsyncStreamIntrinsic::PendingValueQueueClass,
1423        ));
1424    }
1425
1426    if args
1427        .intrinsics
1428        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture))
1429    {
1430        args.intrinsics.extend([
1431            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GlobalFutureMap),
1432            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::NestedFutureSymbol),
1433            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::InternalFutureClass),
1434            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::IsFutureLowerableObject),
1435            &Intrinsic::SymbolResourceRep,
1436            &Intrinsic::GetErrorPayload,
1437            &Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState),
1438            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GenFutureHostInjectFn),
1439            &Intrinsic::Lower(LowerIntrinsic::LowerFlatU32),
1440        ])
1441    }
1442
1443    if args
1444        .intrinsics
1445        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatStringAny))
1446    {
1447        args.intrinsics.extend([
1448            &Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf8),
1449            &Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf16),
1450        ]);
1451    }
1452
1453    if args
1454        .intrinsics
1455        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf8))
1456    {
1457        args.intrinsics
1458            .insert(Intrinsic::String(StringIntrinsic::GlobalTextDecoderUtf8));
1459    }
1460
1461    if args
1462        .intrinsics
1463        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatStringAny))
1464    {
1465        args.intrinsics.extend([
1466            &Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf8),
1467            &Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf16),
1468        ]);
1469    }
1470
1471    if args
1472        .intrinsics
1473        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf8))
1474    {
1475        args.intrinsics
1476            .insert(Intrinsic::String(StringIntrinsic::GlobalTextEncoderUtf8));
1477    }
1478
1479    if args
1480        .intrinsics
1481        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf16))
1482    {
1483        args.intrinsics
1484            .insert(Intrinsic::String(StringIntrinsic::Utf16Decoder));
1485    }
1486
1487    if args
1488        .intrinsics
1489        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatStream))
1490    {
1491        args.intrinsics.insert(Intrinsic::AsyncStream(
1492            AsyncStreamIntrinsic::ExternalStreamClass,
1493        ));
1494    }
1495
1496    if args.intrinsics.contains(&Intrinsic::AsyncTask(
1497        AsyncTaskIntrinsic::CreateNewCurrentTask,
1498    )) || args
1499        .intrinsics
1500        .contains(&Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask))
1501        || args
1502            .intrinsics
1503            .contains(&Intrinsic::AsyncTask(AsyncTaskIntrinsic::ClearCurrentTask))
1504    {
1505        args.intrinsics.extend([
1506            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncTaskClass),
1507            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentTaskMap),
1508        ]);
1509    }
1510
1511    if args
1512        .intrinsics
1513        .contains(&Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamNew))
1514    {
1515        args.intrinsics.extend([
1516            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamMap),
1517            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamTableMap),
1518            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamWritableEndClass),
1519            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamReadableEndClass),
1520        ]);
1521    }
1522
1523    if args.intrinsics.contains(&Intrinsic::AsyncStream(
1524        AsyncStreamIntrinsic::StreamWritableEndClass,
1525    )) || args.intrinsics.contains(&Intrinsic::AsyncStream(
1526        AsyncStreamIntrinsic::StreamReadableEndClass,
1527    )) {
1528        args.intrinsics.extend([
1529            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::InternalStreamClass),
1530            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamEndClass),
1531            &Intrinsic::AsyncEventCodeEnum,
1532        ]);
1533    }
1534
1535    if args.intrinsics.contains(&Intrinsic::AsyncStream(
1536        AsyncStreamIntrinsic::StreamNewFromLift,
1537    )) {
1538        args.intrinsics.extend([
1539            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamMap),
1540            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamTableMap),
1541            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::HostStreamClass),
1542            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::ExternalStreamClass),
1543        ]);
1544    }
1545
1546    if args
1547        .intrinsics
1548        .contains(&Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamWrite))
1549        || args
1550            .intrinsics
1551            .contains(&Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamRead))
1552        || args
1553            .intrinsics
1554            .contains(&Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureWrite))
1555        || args
1556            .intrinsics
1557            .contains(&Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureRead))
1558    {
1559        args.intrinsics.extend([
1560            &Intrinsic::GlobalBufferManager,
1561            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncBlockedConstant),
1562            &Intrinsic::AsyncEventCodeEnum,
1563        ]);
1564    }
1565
1566    if args
1567        .intrinsics
1568        .contains(&Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureNew))
1569    {
1570        args.intrinsics.extend([
1571            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GlobalFutureMap),
1572            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::NestedFutureSymbol),
1573            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GlobalFutureTableMap),
1574            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureWritableEndClass),
1575            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureReadableEndClass),
1576        ]);
1577    }
1578
1579    if args.intrinsics.contains(&Intrinsic::AsyncFuture(
1580        AsyncFutureIntrinsic::FutureNewFromLift,
1581    )) {
1582        args.intrinsics.extend([
1583            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::NestedFutureSymbol),
1584            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GlobalFutureMap),
1585            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::HostFutureClass),
1586        ]);
1587    }
1588
1589    if args.intrinsics.contains(&Intrinsic::AsyncFuture(
1590        AsyncFutureIntrinsic::FutureWritableEndClass,
1591    )) || args.intrinsics.contains(&Intrinsic::AsyncFuture(
1592        AsyncFutureIntrinsic::FutureReadableEndClass,
1593    )) {
1594        args.intrinsics.extend([
1595            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::NestedFutureSymbol),
1596            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::InternalFutureClass),
1597            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureEndClass),
1598            &Intrinsic::AsyncEventCodeEnum,
1599        ]);
1600    }
1601
1602    if args.intrinsics.contains(&Intrinsic::GlobalBufferManager) {
1603        args.intrinsics.extend([&Intrinsic::BufferManagerClass]);
1604    }
1605
1606    if args.intrinsics.contains(&Intrinsic::BufferManagerClass) {
1607        args.intrinsics.extend([&Intrinsic::ManagedBufferClass]);
1608    }
1609
1610    if args.intrinsics.contains(&Intrinsic::AsyncTask(
1611        AsyncTaskIntrinsic::EnterSymmetricSyncGuestCall,
1612    )) || args.intrinsics.contains(&Intrinsic::AsyncTask(
1613        AsyncTaskIntrinsic::ExitSymmetricSyncGuestCall,
1614    )) {
1615        args.intrinsics.extend([
1616            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentComponentIdxs),
1617            &Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState),
1618            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask),
1619            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentTaskIds),
1620            &Intrinsic::ClearGlobalCurrentTaskMetaFn,
1621            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::SymmetricSyncGuestCallStack),
1622        ]);
1623    }
1624
1625    for current_intrinsic in args.intrinsics.iter() {
1626        // Skip already rendered intrinsics (i.e. the early intrinsics)
1627        if rendered_intrinsics.contains(current_intrinsic.name()) {
1628            continue;
1629        }
1630
1631        current_intrinsic.render(&mut output, &args);
1632    }
1633
1634    output
1635}
1636
1637impl Intrinsic {
1638    pub fn get_global_names() -> impl IntoIterator<Item = &'static str> {
1639        JsHelperIntrinsic::get_global_names()
1640            .into_iter()
1641            .chain(vec![
1642                // Intrinsic list exactly as below
1643                "base64Compile",
1644                "clampGuest",
1645                "ComponentError",
1646                "fetchCompile",
1647                "finalizationRegistryCreate",
1648                "getErrorPayload",
1649                "HANDLE_TABLES",
1650                "hasOwnProperty",
1651                "imports",
1652                "instantiateCore",
1653                "isLE",
1654                "scopeId",
1655                "symbolCabiDispose",
1656                "symbolCabiLower",
1657                "symbolDispose",
1658                "symbolAsyncIterator",
1659                "symbolIterator",
1660                "symbolRscHandle",
1661                "symbolRscRep",
1662                "T_FLAG",
1663                "throwInvalidBool",
1664                "throwUninitialized",
1665                // JS Globals / non intrinsic names
1666                "ArrayBuffer",
1667                "BigInt",
1668                "BigInt64Array",
1669                "DataView",
1670                "dv",
1671                "emptyFunc",
1672                "Error",
1673                "fetch",
1674                "Float32Array",
1675                "Float64Array",
1676                "Int32Array",
1677                "Object",
1678                "process",
1679                "String",
1680                "TextDecoder",
1681                "TextEncoder",
1682                "TypeError",
1683                "Uint16Array",
1684                "Uint8Array",
1685                "URL",
1686                "WebAssembly",
1687                "GlobalComponentMemories",
1688            ])
1689    }
1690
1691    pub fn name(&self) -> &'static str {
1692        match self {
1693            Intrinsic::JsHelper(i) => i.name(),
1694            Intrinsic::Conversion(i) => i.name(),
1695            Intrinsic::WebIdl(i) => i.name(),
1696            Intrinsic::String(i) => i.name(),
1697            Intrinsic::ErrCtx(i) => i.name(),
1698            Intrinsic::AsyncTask(i) => i.name(),
1699            Intrinsic::Waitable(i) => i.name(),
1700            Intrinsic::Resource(i) => i.name(),
1701            Intrinsic::Lift(i) => i.name(),
1702            Intrinsic::Lower(i) => i.name(),
1703            Intrinsic::AsyncStream(i) => i.name(),
1704            Intrinsic::AsyncFuture(i) => i.name(),
1705            Intrinsic::Component(i) => i.name(),
1706            Intrinsic::Host(i) => i.name(),
1707
1708            Intrinsic::Base64Compile => "base64Compile",
1709            Intrinsic::ClampGuest => "clampGuest",
1710            Intrinsic::ComponentError => "ComponentError",
1711            Intrinsic::FetchCompile => "fetchCompile",
1712            Intrinsic::FinalizationRegistryCreate => "finalizationRegistryCreate",
1713            Intrinsic::GetErrorPayload => "getErrorPayload",
1714            Intrinsic::GetErrorPayloadString => "getErrorPayloadString",
1715            Intrinsic::HandleTables => "HANDLE_TABLES",
1716            Intrinsic::HasOwnProperty => "hasOwnProperty",
1717            Intrinsic::InstantiateCore => "instantiateCore",
1718            Intrinsic::IsLE => "isLE",
1719            Intrinsic::ScopeId => "SCOPE_ID",
1720
1721            Intrinsic::SymbolCabiDispose => "symbolCabiDispose",
1722            Intrinsic::SymbolCabiLower => "symbolCabiLower",
1723            Intrinsic::SymbolDispose => "symbolDispose",
1724            Intrinsic::SymbolAsyncIterator => "symbolAsyncIterator",
1725            Intrinsic::SymbolIterator => "symbolIterator",
1726            Intrinsic::SymbolResourceHandle => "symbolRscHandle",
1727            Intrinsic::SymbolResourceRep => "symbolRscRep",
1728
1729            Intrinsic::ThrowInvalidBool => "throwInvalidBool",
1730            Intrinsic::ThrowUninitialized => "throwUninitialized",
1731
1732            // Debugging
1733            Intrinsic::DebugLog => "_debugLog",
1734            Intrinsic::PromiseWithResolversPonyfill => "promiseWithResolvers",
1735
1736            // Types
1737            Intrinsic::ConstantI32Min => "I32_MIN",
1738            Intrinsic::ConstantI32Max => "I32_MAX",
1739            Intrinsic::TypeCheckValidI32 => "_typeCheckValidI32",
1740            Intrinsic::TypeCheckAsyncFn => "_typeCheckAsyncFn",
1741            Intrinsic::AsyncFunctionCtor => "ASYNC_FN_CTOR",
1742
1743            // Streams
1744            Intrinsic::PlatformReadableStreamClass => "_PlatformReadableStream",
1745
1746            // Async
1747            Intrinsic::GlobalAsyncDeterminism => "ASYNC_DETERMINISM",
1748            Intrinsic::CoinFlip => "_coinFlip",
1749
1750            // Global current task tracking machinery
1751            Self::GlobalCurrentTaskMeta => "CURRENT_TASK_META",
1752            Self::GetGlobalCurrentTaskMetaFn => "_getGlobalCurrentTaskMeta",
1753            Self::SetGlobalCurrentTaskMetaFn => "_setGlobalCurrentTaskMeta",
1754            Self::WithGlobalCurrentTaskMetaFn => "_withGlobalCurrentTaskMeta",
1755            Self::WithGlobalCurrentTaskMetaFnAsync => "_withGlobalCurrentTaskMetaAsync",
1756            Self::ClearGlobalCurrentTaskMetaFn => "_clearCurrentTask",
1757
1758            // Iteratively saved metadata
1759            Intrinsic::GlobalComponentMemoryMap => "GLOBAL_COMPONENT_MEMORY_MAP",
1760            Intrinsic::RegisterGlobalMemoryForComponent => "registerGlobalMemoryForComponent",
1761            Intrinsic::LookupMemoriesForComponent => "lookupMemoriesForComponent",
1762
1763            // Data structures
1764            Intrinsic::RepTableClass => "RepTable",
1765
1766            // Buffers for managed/synchronized writing to/from component memory
1767            Intrinsic::ManagedBufferClass => "ManagedBuffer",
1768            Intrinsic::BufferManagerClass => "BufferManager",
1769            Intrinsic::GlobalBufferManager => "BUFFER_MGR",
1770
1771            // Helpers for working with async state
1772            Intrinsic::AsyncEventCodeEnum => "ASYNC_EVENT_CODE",
1773        }
1774    }
1775}