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                          if (componentIdx === null || componentIdx === undefined) {{
893                              throw new Error("missing/invalid component idx");
894                          }}
895                          const v = {global_current_task_meta_obj}[componentIdx];
896                          if (v === undefined || v === null) {{
897                              return undefined;
898                          }}
899                          return {{ ...v }};
900                      }}
901                    "#,
902                );
903            }
904
905            Self::SetGlobalCurrentTaskMetaFn => {
906                let set_global_current_task_meta_fn = self.name();
907                let global_current_task_meta_obj = Self::GlobalCurrentTaskMeta.name();
908
909                uwriteln!(
910                    output,
911                    r#"
912                      function {set_global_current_task_meta_fn}(args) {{
913                          if (!args) {{ throw new TypeError('args missing'); }}
914                          if (args.taskID === undefined) {{ throw new TypeError('missing task ID'); }}
915                          if (args.componentIdx === undefined) {{ throw new TypeError('missing component idx'); }}
916                          const {{ taskID, componentIdx }} = args;
917                          return {global_current_task_meta_obj}[componentIdx] = {{ taskID, componentIdx }};
918                      }}
919                    "#,
920                );
921            }
922
923            Self::WithGlobalCurrentTaskMetaFn => {
924                let debug_log_fn = Intrinsic::DebugLog.name();
925                let with_global_current_task_meta_fn = Self::WithGlobalCurrentTaskMetaFn.name();
926                let global_current_task_meta_obj = Self::GlobalCurrentTaskMeta.name();
927
928                output.push_str(&format!(
929                    r#"
930                      function {with_global_current_task_meta_fn}(args) {{
931                          {debug_log_fn}('[{with_global_current_task_meta_fn}()] args', args);
932                          if (!args) {{ throw new TypeError('args missing'); }}
933                          if (args.taskID === undefined) {{ throw new TypeError('missing task ID'); }}
934                          if (args.componentIdx === undefined) {{ throw new TypeError('missing component idx'); }}
935                          if (!args.fn) {{ throw new TypeError('missing fn'); }}
936                          const {{ taskID, componentIdx, fn }} = args;
937
938                          try {{
939                              {global_current_task_meta_obj}[componentIdx] = {{ taskID, componentIdx }};
940                              return fn();
941                          }} catch (err) {{
942                              {debug_log_fn}("error while executing sync callee/callback", {{
943                                  ...args,
944                                  err,
945                              }});
946                              throw err;
947                          }} finally {{
948                              {global_current_task_meta_obj}[componentIdx] = null;
949                          }}
950                      }}
951                    "#,
952                ));
953            }
954
955            // NOTE: this function wrapper/closure intrinsic essentially acts as a
956            // defactor task queue, ensuring that the right "current task" is set when
957            // callees and/or callbacks (WebAssembly functions) run.
958            //
959            // The idea here is to avoid creating *our own* centralized task queue/event loop,
960            // and allow the underlying JS runtime (NodeJS, Browser) to do it's normal scheduling.
961            //
962            // This costs us complexity -- an `await`/`.then()`/etc anywhere else could park a
963            // runtime task and bring us here, in which case we'd be executing *right* before a completely
964            // unrelated task (this matters most when it's multiple tasks in the same component idx)
965            //
966            // e.g.:
967            // 1. [componentIdx 1, task 2] entered -- it's async so this is an `await task.enter()`
968            // 2. JS runtime switches away from that task
969            // 3. [componentIdx 1, task 1] already running, and is about to run it's callee or a callback
970            //
971            // At (3), we must be careful because the "current" thread is *not* [componentIdx 1, task 1] which
972            // is about to try to run it's callback.
973            //
974            // This is complicated because when two tasks run at the same time, we have to ensure that the component
975            // is not exclusively locked by one task. This generally happens @ task.enter(), but an interleaving
976            // of events in which this check happens, then *another* task attempts to exclusively lock could happen.
977            //
978            // In the future, this mechanism may be replaced with a simple event loop that necessarily executes
979            // all pending work serially, with this intrinsic becoming simply queueing work onto that event loop.
980            //
981            Self::WithGlobalCurrentTaskMetaFnAsync => {
982                let debug_log_fn = Intrinsic::DebugLog.name();
983                let with_global_current_task_meta_async_fn =
984                    Self::WithGlobalCurrentTaskMetaFnAsync.name();
985                let global_current_task_meta_obj = Self::GlobalCurrentTaskMeta.name();
986
987                output.push_str(&format!(
988                    r#"
989                      async function {with_global_current_task_meta_async_fn}(args) {{
990                          {debug_log_fn}('[{with_global_current_task_meta_async_fn}()] args', args);
991                          if (!args) {{ throw new TypeError('args missing'); }}
992                          if (args.taskID === undefined) {{ throw new TypeError('missing task ID'); }}
993                          if (args.componentIdx === undefined) {{ throw new TypeError('missing component idx'); }}
994                          if (!args.fn) {{ throw new TypeError('missing fn'); }}
995
996                          const {{ taskID, componentIdx, fn }} = args;
997
998                          try {{
999                              {global_current_task_meta_obj}[componentIdx] = {{ taskID, componentIdx }};
1000                              return await fn();
1001                          }} catch (err) {{
1002                              {debug_log_fn}("error while executing async callee/callback", {{
1003                                  ...args,
1004                                  err,
1005                              }});
1006                              throw err;
1007                          }} finally {{
1008                              {global_current_task_meta_obj}[componentIdx] = null;
1009                          }}
1010                      }}
1011                    "#,
1012                ));
1013            }
1014
1015            Self::ClearGlobalCurrentTaskMetaFn => {
1016                let debug_log_fn = Intrinsic::DebugLog.name();
1017                let clear_global_current_task_meta_fn = Self::ClearGlobalCurrentTaskMetaFn.name();
1018                let global_current_task_meta_obj = Self::GlobalCurrentTaskMeta.name();
1019
1020                output.push_str(&format!(
1021                    r#"
1022                      async function {clear_global_current_task_meta_fn}(args) {{
1023                          {debug_log_fn}('[{clear_global_current_task_meta_fn}()] args', args);
1024                          if (!args) {{ throw new TypeError('args missing'); }}
1025                          if (args.taskID === undefined) {{ throw new TypeError('missing task ID'); }}
1026                          if (args.componentIdx === undefined) {{ throw new TypeError('missing component idx'); }}
1027                          const {{ taskID, componentIdx }} = args;
1028
1029                          const meta = {global_current_task_meta_obj}[componentIdx];
1030                          if (!meta) {{ throw new Error(`missing current task meta for component idx [${{componentIdx}}]`); }}
1031
1032                          if (meta.taskID !== taskID) {{
1033                              throw new Error(`task ID [${{meta.taskID}}] != requested ID [${{taskID}}]`);
1034                          }}
1035                          if (meta.componentIdx !== componentIdx) {{
1036                              throw new Error(`component idx [${{meta.componentIdx}}] != requested idx [${{componentIdx}}]`);
1037                          }}
1038
1039                          {global_current_task_meta_obj}[componentIdx] = null;
1040                      }}
1041                    "#,
1042                ));
1043            }
1044
1045            // TODO(feat): customizable stream classes
1046            Intrinsic::PlatformReadableStreamClass => {
1047                let name = self.name();
1048                uwriteln!(
1049                    output,
1050                    r#"
1051                        if (!ReadableStream) {{
1052                            throw new Error('builtin stream class [ReadableStream] is not available');
1053                        }}
1054                        const {name} = ReadableStream;
1055                    "#
1056                );
1057            }
1058        }
1059    }
1060}
1061
1062/// Profile for determinism to be used by async implementation
1063#[derive(Debug, Default, PartialEq, Eq)]
1064pub enum AsyncDeterminismProfile {
1065    /// Allow random ordering non-determinism
1066    #[default]
1067    Random,
1068
1069    /// Require determinism
1070    #[allow(unused)]
1071    Deterministic,
1072}
1073
1074impl std::fmt::Display for AsyncDeterminismProfile {
1075    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1076        write!(
1077            f,
1078            "{}",
1079            match self {
1080                Self::Deterministic => "deterministic",
1081                Self::Random => "random",
1082            }
1083        )
1084    }
1085}
1086
1087/// Arguments to `render_intrinsics`
1088#[derive(bon::Builder)]
1089#[non_exhaustive]
1090pub struct RenderIntrinsicsArgs<'a> {
1091    /// List of intrinsics being built for use
1092    pub(crate) intrinsics: &'a mut BTreeSet<Intrinsic>,
1093    /// Whether instantiation has occurred
1094    #[builder(default)]
1095    pub(crate) instantiation_occurred: bool,
1096    /// The kind of determinism to use
1097    #[builder(default)]
1098    pub(crate) determinism_profile: AsyncDeterminismProfile,
1099    /// Options provided when performing transpilation
1100    pub(crate) transpile_opts: &'a TranspileOpts,
1101}
1102
1103/// Intrinsics that should be rendered as early as possible
1104const EARLY_INTRINSICS: [Intrinsic; 43] = [
1105    Intrinsic::PromiseWithResolversPonyfill,
1106    Intrinsic::SymbolDispose,
1107    Intrinsic::SymbolAsyncIterator,
1108    Intrinsic::SymbolIterator,
1109    Intrinsic::DebugLog,
1110    Intrinsic::GlobalAsyncDeterminism,
1111    Intrinsic::GlobalComponentMemoryMap,
1112    Intrinsic::GlobalCurrentTaskMeta,
1113    Intrinsic::GetGlobalCurrentTaskMetaFn,
1114    Intrinsic::SetGlobalCurrentTaskMetaFn,
1115    Intrinsic::WithGlobalCurrentTaskMetaFn,
1116    Intrinsic::WithGlobalCurrentTaskMetaFnAsync,
1117    Intrinsic::ClearGlobalCurrentTaskMetaFn,
1118    Intrinsic::LookupMemoriesForComponent,
1119    Intrinsic::RegisterGlobalMemoryForComponent,
1120    Intrinsic::RepTableClass,
1121    Intrinsic::CoinFlip,
1122    Intrinsic::ScopeId,
1123    // Type checking helpers
1124    Intrinsic::ConstantI32Min,
1125    Intrinsic::ConstantI32Max,
1126    Intrinsic::Conversion(ConversionIntrinsic::IsValidNumericPrimitive),
1127    Intrinsic::Conversion(ConversionIntrinsic::RequireValidNumericPrimitive),
1128    Intrinsic::TypeCheckValidI32,
1129    Intrinsic::TypeCheckAsyncFn,
1130    // Resources
1131    Intrinsic::Resource(ResourceIntrinsic::ResourceCallBorrows),
1132    // Async helpers
1133    Intrinsic::AsyncFunctionCtor,
1134    Intrinsic::AsyncTask(AsyncTaskIntrinsic::ClearCurrentTask),
1135    Intrinsic::AsyncTask(AsyncTaskIntrinsic::CurrentTaskMayBlock),
1136    Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentTaskIds),
1137    Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentComponentIdxs),
1138    Intrinsic::AsyncTask(AsyncTaskIntrinsic::UnpackCallbackResult),
1139    Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncSubtaskClass),
1140    // Host helpers
1141    Intrinsic::Host(HostIntrinsic::PrepareCall),
1142    Intrinsic::Host(HostIntrinsic::AsyncStartCall),
1143    Intrinsic::Host(HostIntrinsic::SyncStartCall),
1144    // Waitable helpers
1145    Intrinsic::Waitable(WaitableIntrinsic::WaitableClass),
1146    // Error context helpers
1147    Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalErrCtxTableMap),
1148    // Context get/set are not used via trampolines but are
1149    // `UnsafeIntrinsic`s, so they are mapped for any module that uses them
1150    Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextGet),
1151    Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextSet),
1152    // Required for context.{get,set}
1153    Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentTaskMap),
1154    Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncTaskClass),
1155    Intrinsic::AsyncEventCodeEnum,
1156    Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask),
1157];
1158
1159/// Emits the intrinsic `i` to this file and then returns the name of the
1160/// intrinsic.
1161pub fn render_intrinsics(args: RenderIntrinsicsArgs) -> Source {
1162    let mut output = Source::default();
1163    let mut rendered_intrinsics = HashSet::new();
1164
1165    // Render some early intrinsics
1166    for intrinsic in EARLY_INTRINSICS {
1167        intrinsic.render(&mut output, &args);
1168        rendered_intrinsics.insert(intrinsic.name());
1169    }
1170
1171    // Add intrinsics to the list we must render
1172    if args.intrinsics.contains(&Intrinsic::GetErrorPayload)
1173        || args.intrinsics.contains(&Intrinsic::GetErrorPayloadString)
1174    {
1175        args.intrinsics.insert(Intrinsic::HasOwnProperty);
1176    }
1177    if args
1178        .intrinsics
1179        .contains(&Intrinsic::String(StringIntrinsic::Utf16Encode))
1180    {
1181        args.intrinsics.insert(Intrinsic::IsLE);
1182    }
1183
1184    if args
1185        .intrinsics
1186        .contains(&Intrinsic::Conversion(ConversionIntrinsic::F32ToI32))
1187        || args
1188            .intrinsics
1189            .contains(&Intrinsic::Conversion(ConversionIntrinsic::I32ToF32))
1190    {
1191        output.push_str(
1192            "
1193            const i32ToF32I = new Int32Array(1);
1194            const i32ToF32F = new Float32Array(i32ToF32I.buffer);
1195        ",
1196        );
1197    }
1198
1199    if args
1200        .intrinsics
1201        .contains(&Intrinsic::Conversion(ConversionIntrinsic::F64ToI64))
1202        || args
1203            .intrinsics
1204            .contains(&Intrinsic::Conversion(ConversionIntrinsic::I64ToF64))
1205    {
1206        output.push_str(
1207            "
1208            const i64ToF64I = new BigInt64Array(1);
1209            const i64ToF64F = new Float64Array(i64ToF64I.buffer);
1210        ",
1211        );
1212    }
1213
1214    if args.intrinsics.contains(&Intrinsic::Resource(
1215        ResourceIntrinsic::ResourceTransferBorrow,
1216    )) || args.intrinsics.contains(&Intrinsic::Resource(
1217        ResourceIntrinsic::ResourceTransferBorrowValidLifting,
1218    )) {
1219        args.intrinsics.insert(Intrinsic::Resource(
1220            ResourceIntrinsic::ResourceTableCreateBorrow,
1221        ));
1222    }
1223
1224    if args
1225        .intrinsics
1226        .contains(&Intrinsic::String(StringIntrinsic::Utf8Encode))
1227        || args
1228            .intrinsics
1229            .contains(&Intrinsic::String(StringIntrinsic::Utf8EncodeAsync))
1230    {
1231        args.intrinsics.extend([
1232            &Intrinsic::IsLE,
1233            &Intrinsic::String(StringIntrinsic::GlobalTextEncoderUtf8),
1234        ]);
1235    }
1236
1237    if args
1238        .intrinsics
1239        .contains(&Intrinsic::String(StringIntrinsic::Utf16Encode))
1240        || args
1241            .intrinsics
1242            .contains(&Intrinsic::String(StringIntrinsic::Utf16EncodeAsync))
1243    {
1244        args.intrinsics.extend([&Intrinsic::IsLE]);
1245    }
1246
1247    // Attempting to perform a debug message hoist will require string encoding to memory
1248    if args.intrinsics.contains(&Intrinsic::ErrCtx(
1249        ErrCtxIntrinsic::ErrorContextDebugMessage,
1250    )) {
1251        args.intrinsics.extend([
1252            &Intrinsic::String(StringIntrinsic::Utf8Encode),
1253            &Intrinsic::String(StringIntrinsic::Utf16Encode),
1254            &Intrinsic::ErrCtx(ErrCtxIntrinsic::GetLocalTable),
1255        ]);
1256    }
1257
1258    if args
1259        .intrinsics
1260        .contains(&Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextNew))
1261    {
1262        args.intrinsics.extend([
1263            &Intrinsic::ErrCtx(ErrCtxIntrinsic::ComponentGlobalTable),
1264            &Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalRefCountAdd),
1265            &Intrinsic::ErrCtx(ErrCtxIntrinsic::ReserveGlobalRep),
1266            &Intrinsic::ErrCtx(ErrCtxIntrinsic::CreateLocalHandle),
1267            &Intrinsic::ErrCtx(ErrCtxIntrinsic::GetLocalTable),
1268        ]);
1269    }
1270
1271    if args.intrinsics.contains(&Intrinsic::ErrCtx(
1272        ErrCtxIntrinsic::ErrorContextDebugMessage,
1273    )) {
1274        args.intrinsics.extend([
1275            &Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalRefCountAdd),
1276            &Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextDrop),
1277            &Intrinsic::ErrCtx(ErrCtxIntrinsic::GetLocalTable),
1278        ]);
1279    }
1280
1281    if args
1282        .intrinsics
1283        .contains(&Intrinsic::AsyncTask(AsyncTaskIntrinsic::DriverLoop))
1284    {
1285        args.intrinsics.extend([
1286            &Intrinsic::TypeCheckValidI32,
1287            &Intrinsic::Conversion(ConversionIntrinsic::ToInt32),
1288            &Intrinsic::Component(ComponentIntrinsic::ComponentStateSetAllError),
1289        ]);
1290    }
1291
1292    if args.intrinsics.contains(&Intrinsic::Component(
1293        ComponentIntrinsic::GetOrCreateAsyncState,
1294    )) {
1295        args.intrinsics.extend([&Intrinsic::RepTableClass]);
1296    }
1297
1298    if args
1299        .intrinsics
1300        .contains(&Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncTaskClass))
1301    {
1302        args.intrinsics.extend([
1303            &Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState),
1304            &Intrinsic::Component(ComponentIntrinsic::GlobalAsyncStateMap),
1305            &Intrinsic::RepTableClass,
1306            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncSubtaskClass),
1307            &Intrinsic::Waitable(WaitableIntrinsic::WaitableClass),
1308        ]);
1309    }
1310
1311    if args
1312        .intrinsics
1313        .contains(&Intrinsic::Waitable(WaitableIntrinsic::WaitableSetNew))
1314    {
1315        args.intrinsics
1316            .extend([&Intrinsic::Waitable(WaitableIntrinsic::WaitableSetClass)]);
1317    }
1318
1319    if args
1320        .intrinsics
1321        .contains(&Intrinsic::Waitable(WaitableIntrinsic::WaitableSetPoll))
1322        || args
1323            .intrinsics
1324            .contains(&Intrinsic::Waitable(WaitableIntrinsic::WaitableSetWait))
1325    {
1326        args.intrinsics
1327            .extend([&Intrinsic::Host(HostIntrinsic::StoreEventInComponentMemory)]);
1328    }
1329
1330    if args
1331        .intrinsics
1332        .contains(&Intrinsic::Waitable(WaitableIntrinsic::WaitableSetDrop))
1333    {
1334        args.intrinsics
1335            .extend([&Intrinsic::Waitable(WaitableIntrinsic::RemoveWaitableSet)]);
1336    }
1337
1338    if args.intrinsics.contains(&Intrinsic::Component(
1339        ComponentIntrinsic::GetOrCreateAsyncState,
1340    )) {
1341        args.intrinsics.extend([
1342            &Intrinsic::Component(ComponentIntrinsic::ComponentAsyncStateClass),
1343            &Intrinsic::Component(ComponentIntrinsic::GlobalAsyncStateMap),
1344        ]);
1345    }
1346
1347    if args.intrinsics.contains(&Intrinsic::Component(
1348        ComponentIntrinsic::ComponentAsyncStateClass,
1349    )) {
1350        args.intrinsics.extend([&Intrinsic::AsyncStream(
1351            AsyncStreamIntrinsic::GlobalStreamMap,
1352        )]);
1353    }
1354
1355    if args
1356        .intrinsics
1357        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatResult))
1358        | args
1359            .intrinsics
1360            .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatOption))
1361        | args
1362            .intrinsics
1363            .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatOption))
1364    {
1365        args.intrinsics
1366            .extend([&Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant)]);
1367    }
1368
1369    if args
1370        .intrinsics
1371        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant))
1372    {
1373        args.intrinsics.extend([
1374            &Intrinsic::Lift(LiftIntrinsic::LiftFlatU8),
1375            &Intrinsic::Lift(LiftIntrinsic::LiftFlatU16),
1376            &Intrinsic::Lift(LiftIntrinsic::LiftFlatU32),
1377            &Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64),
1378        ]);
1379    }
1380
1381    if args
1382        .intrinsics
1383        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatResult))
1384    {
1385        args.intrinsics
1386            .insert(Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant));
1387    }
1388
1389    if args
1390        .intrinsics
1391        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatOption))
1392    {
1393        args.intrinsics
1394            .insert(Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant));
1395    }
1396
1397    if args
1398        .intrinsics
1399        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant))
1400    {
1401        args.intrinsics.extend([
1402            &Intrinsic::Lower(LowerIntrinsic::LowerFlatU8),
1403            &Intrinsic::Lower(LowerIntrinsic::LowerFlatU16),
1404            &Intrinsic::Lower(LowerIntrinsic::LowerFlatU32),
1405        ]);
1406    }
1407
1408    if args
1409        .intrinsics
1410        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatStream))
1411    {
1412        args.intrinsics.extend([
1413            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamMap),
1414            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::ExternalStreamClass),
1415            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::InternalStreamClass),
1416            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::IsStreamLowerableObject),
1417            &Intrinsic::SymbolResourceRep,
1418            &Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState),
1419            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GenReadFnFromLowerableStream),
1420            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GenStreamHostInjectFn),
1421            &Intrinsic::Lower(LowerIntrinsic::LowerFlatU32),
1422        ])
1423    }
1424
1425    if args.intrinsics.contains(&Intrinsic::AsyncStream(
1426        AsyncStreamIntrinsic::GenStreamHostInjectFn,
1427    )) {
1428        args.intrinsics.insert(Intrinsic::AsyncStream(
1429            AsyncStreamIntrinsic::PendingValueQueueClass,
1430        ));
1431    }
1432
1433    if args
1434        .intrinsics
1435        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture))
1436    {
1437        args.intrinsics.extend([
1438            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GlobalFutureMap),
1439            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::NestedFutureSymbol),
1440            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::InternalFutureClass),
1441            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::IsFutureLowerableObject),
1442            &Intrinsic::SymbolResourceRep,
1443            &Intrinsic::GetErrorPayload,
1444            &Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState),
1445            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GenFutureHostInjectFn),
1446            &Intrinsic::Lower(LowerIntrinsic::LowerFlatU32),
1447        ])
1448    }
1449
1450    if args
1451        .intrinsics
1452        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatStringAny))
1453    {
1454        args.intrinsics.extend([
1455            &Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf8),
1456            &Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf16),
1457        ]);
1458    }
1459
1460    if args
1461        .intrinsics
1462        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf8))
1463    {
1464        args.intrinsics
1465            .insert(Intrinsic::String(StringIntrinsic::GlobalTextDecoderUtf8));
1466    }
1467
1468    if args
1469        .intrinsics
1470        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatStringAny))
1471    {
1472        args.intrinsics.extend([
1473            &Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf8),
1474            &Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf16),
1475        ]);
1476    }
1477
1478    if args
1479        .intrinsics
1480        .contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf8))
1481    {
1482        args.intrinsics
1483            .insert(Intrinsic::String(StringIntrinsic::GlobalTextEncoderUtf8));
1484    }
1485
1486    if args
1487        .intrinsics
1488        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf16))
1489    {
1490        args.intrinsics
1491            .insert(Intrinsic::String(StringIntrinsic::Utf16Decoder));
1492    }
1493
1494    if args
1495        .intrinsics
1496        .contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatStream))
1497    {
1498        args.intrinsics.insert(Intrinsic::AsyncStream(
1499            AsyncStreamIntrinsic::ExternalStreamClass,
1500        ));
1501    }
1502
1503    if args.intrinsics.contains(&Intrinsic::AsyncTask(
1504        AsyncTaskIntrinsic::CreateNewCurrentTask,
1505    )) || args
1506        .intrinsics
1507        .contains(&Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask))
1508        || args
1509            .intrinsics
1510            .contains(&Intrinsic::AsyncTask(AsyncTaskIntrinsic::ClearCurrentTask))
1511    {
1512        args.intrinsics.extend([
1513            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncTaskClass),
1514            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentTaskMap),
1515        ]);
1516    }
1517
1518    if args
1519        .intrinsics
1520        .contains(&Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamNew))
1521    {
1522        args.intrinsics.extend([
1523            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamMap),
1524            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamTableMap),
1525            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamWritableEndClass),
1526            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamReadableEndClass),
1527        ]);
1528    }
1529
1530    if args.intrinsics.contains(&Intrinsic::AsyncStream(
1531        AsyncStreamIntrinsic::StreamWritableEndClass,
1532    )) || args.intrinsics.contains(&Intrinsic::AsyncStream(
1533        AsyncStreamIntrinsic::StreamReadableEndClass,
1534    )) {
1535        args.intrinsics.extend([
1536            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::InternalStreamClass),
1537            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamEndClass),
1538            &Intrinsic::AsyncEventCodeEnum,
1539        ]);
1540    }
1541
1542    if args.intrinsics.contains(&Intrinsic::AsyncStream(
1543        AsyncStreamIntrinsic::StreamNewFromLift,
1544    )) {
1545        args.intrinsics.extend([
1546            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamMap),
1547            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamTableMap),
1548            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::HostStreamClass),
1549            &Intrinsic::AsyncStream(AsyncStreamIntrinsic::ExternalStreamClass),
1550            &Intrinsic::GlobalBufferManager,
1551        ]);
1552    }
1553
1554    if args
1555        .intrinsics
1556        .contains(&Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamWrite))
1557        || args
1558            .intrinsics
1559            .contains(&Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamRead))
1560        || args
1561            .intrinsics
1562            .contains(&Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureWrite))
1563        || args
1564            .intrinsics
1565            .contains(&Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureRead))
1566    {
1567        args.intrinsics.extend([
1568            &Intrinsic::GlobalBufferManager,
1569            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::AsyncBlockedConstant),
1570            &Intrinsic::AsyncEventCodeEnum,
1571        ]);
1572    }
1573
1574    if args
1575        .intrinsics
1576        .contains(&Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureNew))
1577    {
1578        args.intrinsics.extend([
1579            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GlobalFutureMap),
1580            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::NestedFutureSymbol),
1581            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GlobalFutureTableMap),
1582            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureWritableEndClass),
1583            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureReadableEndClass),
1584        ]);
1585    }
1586
1587    if args.intrinsics.contains(&Intrinsic::AsyncFuture(
1588        AsyncFutureIntrinsic::FutureNewFromLift,
1589    )) {
1590        args.intrinsics.extend([
1591            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::NestedFutureSymbol),
1592            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GlobalFutureMap),
1593            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::HostFutureClass),
1594            &Intrinsic::GlobalBufferManager,
1595        ]);
1596    }
1597
1598    if args.intrinsics.contains(&Intrinsic::AsyncFuture(
1599        AsyncFutureIntrinsic::FutureWritableEndClass,
1600    )) || args.intrinsics.contains(&Intrinsic::AsyncFuture(
1601        AsyncFutureIntrinsic::FutureReadableEndClass,
1602    )) {
1603        args.intrinsics.extend([
1604            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::NestedFutureSymbol),
1605            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::InternalFutureClass),
1606            &Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureEndClass),
1607            &Intrinsic::AsyncEventCodeEnum,
1608        ]);
1609    }
1610
1611    if args.intrinsics.contains(&Intrinsic::GlobalBufferManager) {
1612        args.intrinsics.extend([&Intrinsic::BufferManagerClass]);
1613    }
1614
1615    if args.intrinsics.contains(&Intrinsic::BufferManagerClass) {
1616        args.intrinsics.extend([&Intrinsic::ManagedBufferClass]);
1617    }
1618
1619    if args.intrinsics.contains(&Intrinsic::AsyncTask(
1620        AsyncTaskIntrinsic::EnterSymmetricSyncGuestCall,
1621    )) || args.intrinsics.contains(&Intrinsic::AsyncTask(
1622        AsyncTaskIntrinsic::ExitSymmetricSyncGuestCall,
1623    )) {
1624        args.intrinsics.extend([
1625            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentComponentIdxs),
1626            &Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState),
1627            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask),
1628            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::GlobalAsyncCurrentTaskIds),
1629            &Intrinsic::ClearGlobalCurrentTaskMetaFn,
1630            &Intrinsic::AsyncTask(AsyncTaskIntrinsic::SymmetricSyncGuestCallStack),
1631        ]);
1632    }
1633
1634    for current_intrinsic in args.intrinsics.iter() {
1635        // Skip already rendered intrinsics (i.e. the early intrinsics)
1636        if rendered_intrinsics.contains(current_intrinsic.name()) {
1637            continue;
1638        }
1639
1640        current_intrinsic.render(&mut output, &args);
1641    }
1642
1643    output
1644}
1645
1646impl Intrinsic {
1647    pub fn get_global_names() -> impl IntoIterator<Item = &'static str> {
1648        JsHelperIntrinsic::get_global_names()
1649            .into_iter()
1650            .chain(vec![
1651                // Intrinsic list exactly as below
1652                "base64Compile",
1653                "clampGuest",
1654                "ComponentError",
1655                "fetchCompile",
1656                "finalizationRegistryCreate",
1657                "getErrorPayload",
1658                "HANDLE_TABLES",
1659                "hasOwnProperty",
1660                "imports",
1661                "instantiateCore",
1662                "isLE",
1663                "scopeId",
1664                "symbolCabiDispose",
1665                "symbolCabiLower",
1666                "symbolDispose",
1667                "symbolAsyncIterator",
1668                "symbolIterator",
1669                "symbolRscHandle",
1670                "symbolRscRep",
1671                "T_FLAG",
1672                "throwInvalidBool",
1673                "throwUninitialized",
1674                // JS Globals / non intrinsic names
1675                "ArrayBuffer",
1676                "BigInt",
1677                "BigInt64Array",
1678                "DataView",
1679                "dv",
1680                "emptyFunc",
1681                "Error",
1682                "fetch",
1683                "Float32Array",
1684                "Float64Array",
1685                "Int32Array",
1686                "Object",
1687                "process",
1688                "String",
1689                "TextDecoder",
1690                "TextEncoder",
1691                "TypeError",
1692                "Uint16Array",
1693                "Uint8Array",
1694                "URL",
1695                "WebAssembly",
1696                "GlobalComponentMemories",
1697            ])
1698    }
1699
1700    pub fn name(&self) -> &'static str {
1701        match self {
1702            Intrinsic::JsHelper(i) => i.name(),
1703            Intrinsic::Conversion(i) => i.name(),
1704            Intrinsic::WebIdl(i) => i.name(),
1705            Intrinsic::String(i) => i.name(),
1706            Intrinsic::ErrCtx(i) => i.name(),
1707            Intrinsic::AsyncTask(i) => i.name(),
1708            Intrinsic::Waitable(i) => i.name(),
1709            Intrinsic::Resource(i) => i.name(),
1710            Intrinsic::Lift(i) => i.name(),
1711            Intrinsic::Lower(i) => i.name(),
1712            Intrinsic::AsyncStream(i) => i.name(),
1713            Intrinsic::AsyncFuture(i) => i.name(),
1714            Intrinsic::Component(i) => i.name(),
1715            Intrinsic::Host(i) => i.name(),
1716
1717            Intrinsic::Base64Compile => "base64Compile",
1718            Intrinsic::ClampGuest => "clampGuest",
1719            Intrinsic::ComponentError => "ComponentError",
1720            Intrinsic::FetchCompile => "fetchCompile",
1721            Intrinsic::FinalizationRegistryCreate => "finalizationRegistryCreate",
1722            Intrinsic::GetErrorPayload => "getErrorPayload",
1723            Intrinsic::GetErrorPayloadString => "getErrorPayloadString",
1724            Intrinsic::HandleTables => "HANDLE_TABLES",
1725            Intrinsic::HasOwnProperty => "hasOwnProperty",
1726            Intrinsic::InstantiateCore => "instantiateCore",
1727            Intrinsic::IsLE => "isLE",
1728            Intrinsic::ScopeId => "SCOPE_ID",
1729
1730            Intrinsic::SymbolCabiDispose => "symbolCabiDispose",
1731            Intrinsic::SymbolCabiLower => "symbolCabiLower",
1732            Intrinsic::SymbolDispose => "symbolDispose",
1733            Intrinsic::SymbolAsyncIterator => "symbolAsyncIterator",
1734            Intrinsic::SymbolIterator => "symbolIterator",
1735            Intrinsic::SymbolResourceHandle => "symbolRscHandle",
1736            Intrinsic::SymbolResourceRep => "symbolRscRep",
1737
1738            Intrinsic::ThrowInvalidBool => "throwInvalidBool",
1739            Intrinsic::ThrowUninitialized => "throwUninitialized",
1740
1741            // Debugging
1742            Intrinsic::DebugLog => "_debugLog",
1743            Intrinsic::PromiseWithResolversPonyfill => "promiseWithResolvers",
1744
1745            // Types
1746            Intrinsic::ConstantI32Min => "I32_MIN",
1747            Intrinsic::ConstantI32Max => "I32_MAX",
1748            Intrinsic::TypeCheckValidI32 => "_typeCheckValidI32",
1749            Intrinsic::TypeCheckAsyncFn => "_typeCheckAsyncFn",
1750            Intrinsic::AsyncFunctionCtor => "ASYNC_FN_CTOR",
1751
1752            // Streams
1753            Intrinsic::PlatformReadableStreamClass => "_PlatformReadableStream",
1754
1755            // Async
1756            Intrinsic::GlobalAsyncDeterminism => "ASYNC_DETERMINISM",
1757            Intrinsic::CoinFlip => "_coinFlip",
1758
1759            // Global current task tracking machinery
1760            Self::GlobalCurrentTaskMeta => "CURRENT_TASK_META",
1761            Self::GetGlobalCurrentTaskMetaFn => "_getGlobalCurrentTaskMeta",
1762            Self::SetGlobalCurrentTaskMetaFn => "_setGlobalCurrentTaskMeta",
1763            Self::WithGlobalCurrentTaskMetaFn => "_withGlobalCurrentTaskMeta",
1764            Self::WithGlobalCurrentTaskMetaFnAsync => "_withGlobalCurrentTaskMetaAsync",
1765            Self::ClearGlobalCurrentTaskMetaFn => "_clearCurrentTask",
1766
1767            // Iteratively saved metadata
1768            Intrinsic::GlobalComponentMemoryMap => "GLOBAL_COMPONENT_MEMORY_MAP",
1769            Intrinsic::RegisterGlobalMemoryForComponent => "registerGlobalMemoryForComponent",
1770            Intrinsic::LookupMemoriesForComponent => "lookupMemoriesForComponent",
1771
1772            // Data structures
1773            Intrinsic::RepTableClass => "RepTable",
1774
1775            // Buffers for managed/synchronized writing to/from component memory
1776            Intrinsic::ManagedBufferClass => "ManagedBuffer",
1777            Intrinsic::BufferManagerClass => "BufferManager",
1778            Intrinsic::GlobalBufferManager => "BUFFER_MGR",
1779
1780            // Helpers for working with async state
1781            Intrinsic::AsyncEventCodeEnum => "ASYNC_EVENT_CODE",
1782        }
1783    }
1784}