11//! Intrinsics that represent helpers that should be used from JS code
22
3- use crate :: { intrinsics:: RenderIntrinsicsArgs , source:: Source } ;
3+ use std:: fmt:: Write ;
4+
5+ use crate :: intrinsics:: p3:: async_task:: AsyncTaskIntrinsic ;
6+ use crate :: {
7+ intrinsics:: { Intrinsic , RenderIntrinsicsArgs } ,
8+ source:: Source ,
9+ uwriteln,
10+ } ;
411
512/// This enum contains intrinsics that function as JS helpers
613///
@@ -13,24 +20,37 @@ pub enum JsHelperIntrinsic {
1320
1421 /// A helper function that creates a JS `DataView` from a given memory index
1522 DataView ,
23+
24+ /// Run a generator function synchronously until it yields a promise.
25+ MaybeSyncGeneratorFn ,
26+
27+ /// Bypass the JSPI boundary when a host import's caller cannot block.
28+ ConditionalSuspendingHostImportFn ,
1629}
1730
1831impl JsHelperIntrinsic {
1932 /// Retrieve global names for
2033 pub fn get_global_names ( ) -> impl IntoIterator < Item = & ' static str > {
21- [ "emptyFunc" , "dataView" ]
34+ [
35+ "emptyFunc" ,
36+ "dataView" ,
37+ "_maybeSyncGenerator" ,
38+ "_conditionalSuspendingHostImport" ,
39+ ]
2240 }
2341
2442 /// Get the name for the intrinsic
2543 pub fn name ( & self ) -> & ' static str {
2644 match self {
2745 Self :: EmptyFunc => "emptyFunc" ,
2846 Self :: DataView => "dataView" ,
47+ Self :: MaybeSyncGeneratorFn => "_maybeSyncGenerator" ,
48+ Self :: ConditionalSuspendingHostImportFn => "_conditionalSuspendingHostImport" ,
2949 }
3050 }
3151
3252 /// Render an intrinsic to a string
33- pub fn render ( & self , output : & mut Source , _args : & RenderIntrinsicsArgs ) {
53+ pub fn render ( & self , output : & mut Source , args : & RenderIntrinsicsArgs ) {
3454 match self {
3555 Self :: EmptyFunc => output. push_str ( "
3656 const emptyFunc = () => {};
@@ -39,6 +59,66 @@ impl JsHelperIntrinsic {
3959 let dv = new DataView(new ArrayBuffer());
4060 const dataView = mem => dv.buffer === mem.buffer ? dv : dv = new DataView(mem.buffer);
4161 " ) ,
62+ // Wrap a generator function, which yields where an async function would await,
63+ // so that it runs synchronously until it yields a promise. A host import that
64+ // answers synchronously then returns synchronously, which a task that cannot
65+ // block (e.g. a resource destructor) requires.
66+ Self :: MaybeSyncGeneratorFn => {
67+ let maybe_sync_generator_fn = args. require_intrinsic ( Self :: MaybeSyncGeneratorFn ) ;
68+
69+ output. push_str ( & format ! (
70+ r#"
71+ function {maybe_sync_generator_fn}(genFn) {{
72+ return function (...args) {{
73+ const gen = genFn.apply(this, args);
74+ const step = (method, value) => {{
75+ while (true) {{
76+ const {{ value: yielded, done }} = gen[method](value);
77+ if (done) {{ return yielded; }}
78+ if (yielded === null ||
79+ (typeof yielded !== 'object' && typeof yielded !== 'function') ||
80+ typeof yielded.then !== 'function') {{
81+ method = 'next';
82+ value = yielded;
83+ continue;
84+ }}
85+ return Promise.resolve(yielded).then(
86+ res => step('next', res),
87+ err => step('throw', err),
88+ );
89+ }}
90+ }};
91+ return step('next', undefined);
92+ }};
93+ }}
94+ "# ,
95+ ) ) ;
96+ }
97+
98+ Self :: ConditionalSuspendingHostImportFn => {
99+ let wrapper = args. require_intrinsic ( * self ) ;
100+ let current_meta = args. require_intrinsic ( Intrinsic :: GlobalCurrentThreadMeta ) ;
101+ let get_thread = args. require_intrinsic ( AsyncTaskIntrinsic :: GetCurrentThread ) ;
102+ uwriteln ! (
103+ output,
104+ r#"
105+ function {wrapper}(module, componentIdx, fn) {{
106+ return new WebAssembly.Instance(module, {{ '': {{
107+ mayBlock: () => {{
108+ const saved = {current_meta}[componentIdx];
109+ const thread = saved
110+ ? {get_thread}(saved.componentIdx, saved.threadID)
111+ : null;
112+ return thread?.task.mayBlock() ?? false;
113+ }},
114+ fast: fn,
115+ slow: new WebAssembly.Suspending(fn),
116+ }} }}).exports.run;
117+ }}
118+ "# ,
119+ ) ;
120+ }
121+
42122 }
43123 }
44124}
0 commit comments