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