Skip to content

Commit 12066e3

Browse files
refactor(bindgen): factor out strewam write injection, use w/ lower
1 parent 576e6b8 commit 12066e3

5 files changed

Lines changed: 290 additions & 106 deletions

File tree

crates/js-component-bindgen/src/function_bindgen.rs

Lines changed: 13 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2337,7 +2337,7 @@ impl Bindgen for FunctionBindgen<'_> {
23372337
let tmp = self.tmp();
23382338
match (imported, is_own) {
23392339
// imported owned/borrowed guest resource
2340-
(_imported @ true, _owned @ _) => {
2340+
(_imported @ true, _owned) => {
23412341
uwrite!(
23422342
self.src,
23432343
r#"
@@ -2525,6 +2525,9 @@ impl Bindgen for FunctionBindgen<'_> {
25252525
let get_or_create_async_state_fn = self.intrinsic(Intrinsic::Component(
25262526
ComponentIntrinsic::GetOrCreateAsyncState,
25272527
));
2528+
let gen_host_inject_fn = self.intrinsic(Intrinsic::AsyncStream(
2529+
AsyncStreamIntrinsic::GenHostInjectFn,
2530+
));
25282531

25292532
// Build the lowering function for the type produced by the stream
25302533
let type_id = &crate::dealias(self.resolve, *ty);
@@ -2650,70 +2653,25 @@ impl Bindgen for FunctionBindgen<'_> {
26502653
}},
26512654
}});
26522655
2653-
const doNothing{tmp} = () => {{}};
2654-
const resetWriteEndToIdle{tmp} = () => {{
2655-
// After the write is finished, we consume the event that was generated
2656-
// by the just-in-time write (and the subsequent read), if one was generated
2657-
if (hostWriteEnd{tmp}.hasPendingEvent()) {{ hostWriteEnd{tmp}.getPendingEvent(); }}
2658-
}};
2659-
let genHostInjectFn = (readFn) => {{
2660-
let done = false;
2661-
2662-
return async (args) => {{
2663-
let {{ count }} = args;
2664-
if (count < 0) {{ throw new Error('invalid count'); }}
2665-
if (count === 0) {{ return doNothing{tmp}; }}
2666-
2667-
// If we get another read when done is already set, that was
2668-
// the case of a iterator that returned a final value
2669-
// along with `done: true`
2670-
if (done) {{
2671-
hostWriteEnd{tmp}.getPendingEvent();
2672-
hostWriteEnd{tmp}.drop();
2673-
return doNothing{tmp};
2674-
}}
2675-
2676-
if (hostWriteEnd{tmp}.isDoneState()) {{
2677-
return doNothing{tmp};
2678-
}}
2679-
2680-
const values = [];
2681-
while (count > 0 && !done) {{
2682-
const res = await readFn();
2683-
if (res.value !== undefined) {{ values.push(res.value); }}
2684-
done = res.done;
2685-
if (done) {{ break; }}
2686-
count -= 1;
2687-
}}
2688-
2689-
// Iterator provided `done: true` with no final value
2690-
if (done && values.length === 0) {{
2691-
hostWriteEnd{tmp}.getPendingEvent();
2692-
hostWriteEnd{tmp}.drop();
2693-
return doNothing{tmp};
2694-
}}
2695-
2696-
await hostWriteEnd{tmp}.write(values);
2697-
2698-
return resetWriteEndToIdle{tmp};
2699-
}};
2700-
}};
2701-
2702-
let readFn;
2656+
let readFn{tmp};
27032657
if ({async_iterator_symbol} in {stream_arg}) {{
27042658
let asyncIterator = {stream_arg}[{async_iterator_symbol}]();
2705-
readFn = () => asyncIterator.next();
2659+
readFn{tmp} = () => asyncIterator.next();
27062660
}} else if ({iterator_symbol} in {stream_arg}) {{
27072661
let iterator = {stream_arg}[{iterator_symbol}]();
2708-
readFn = async () => iterator.next();
2662+
readFn{tmp} = async () => iterator.next();
27092663
}} else if ({stream_arg} instanceof {external_readable_stream_class}) {{
27102664
// At this point we're dealing with a readable stream that *somehow *does not*
27112665
// implement the async iterator protocol.
27122666
const lockedReader = {stream_arg}.getReader();
2713-
readFn = () => lockedReader.read();
2667+
readFn{tmp} = () => lockedReader.read();
27142668
}}
27152669
2716-
readEnd{tmp}.setHostInjectFn(genHostInjectFn(readFn));
2670+
const hostInjectFn = {gen_host_inject_fn}({{
2671+
readFn: readFn{tmp},
2672+
hostWriteEnd: hostWriteEnd{tmp},
2673+
}});
2674+
readEnd{tmp}.setHostInjectFn(hostInjectFn);
27172675
27182676
const {lowered_stream_waitable_idx} = readEnd{tmp}.waitableIdx();
27192677
"#

crates/js-component-bindgen/src/intrinsics/lower.rs

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -986,48 +986,79 @@ impl LowerIntrinsic {
986986
let global_stream_map = AsyncStreamIntrinsic::GlobalStreamMap.name();
987987
let external_stream_class = AsyncStreamIntrinsic::ExternalStreamClass.name();
988988
let internal_stream_class = AsyncStreamIntrinsic::InternalStreamClass.name();
989+
let is_stream_lowerable_object =
990+
AsyncStreamIntrinsic::IsStreamLowerableObject.name();
991+
let symbol_cabi_rep = Intrinsic::SymbolResourceRep.name();
992+
let get_or_create_async_state_fn = ComponentIntrinsic::GetOrCreateAsyncState.name();
993+
let gen_read_fn_from_lowerable_stream_fn =
994+
Intrinsic::AsyncStream(AsyncStreamIntrinsic::GenReadFnFromLowerableStream)
995+
.name();
996+
let gen_host_inject_fn =
997+
Intrinsic::AsyncStream(AsyncStreamIntrinsic::GenHostInjectFn).name();
998+
let lower_u32_fn = Self::LowerFlatU32.name();
989999

9901000
output.push_str(&format!(
9911001
r#"
9921002
function {lower_flat_stream_fn}(meta) {{
9931003
const {{
994-
streamTableIdx,
9951004
componentIdx,
996-
isBorrowedType,
997-
isNoneType,
998-
isNumericTypeJs,
1005+
streamTableIdx,
1006+
elemMeta,
9991007
}} = meta;
10001008
10011009
return function {lower_flat_stream_fn}Inner(ctx) {{
10021010
{debug_log_fn}('[{lower_flat_stream_fn}()] args', {{ ctx }});
10031011
1004-
// TODO(fix): This stream could be a stream from the host, which is
1005-
// any async-iterator capable thing (see Instruction::StreamLower),
1006-
// not only an ExternalStream which is used by the guest???
1007-
1008-
const externalStream = ctx.vals[0];
1009-
if (!externalStream || !(externalStream instanceof {external_stream_class})) {{
1010-
throw new Error("invalid external stream value");
1011-
}}
1012+
const stream = ctx.vals[0];
1013+
if (!stream) {{ throw new Error("missing external stream value"); }}
10121014
1013-
const globalRep = externalStream.globalRep();
1014-
const internalStream = {global_stream_map}.get(globalRep);
1015-
if (!internalStream || !(internalStream instanceof {internal_stream_class})) {{
1016-
throw new Error(`failed to find internal stream with rep [${{globalRep}}]`);
1015+
let globalRep;
1016+
let waitableIdx;
1017+
if (stream instanceof {external_stream_class}) {{
1018+
globalRep = stream[{symbol_cabi_rep}];
1019+
const internalStream = {global_stream_map}.get(globalRep);
1020+
if (!internalStream || !(internalStream instanceof {internal_stream_class})) {{
1021+
throw new Error(`failed to find internal stream with rep [${{globalRep}}]`);
1022+
}}
1023+
waitableIdx = internalStream.readEnd().waitableIdx();
1024+
}} else if ({is_stream_lowerable_object}(stream)) {{
1025+
globalRep = stream[{symbol_cabi_rep}];
1026+
1027+
if (globalRep) {{
1028+
const hostStream = {global_stream_map}.get(globalRep);
1029+
if (!hostStream) {{
1030+
throw new Error(`missing host stream with global rep [${{globalRep}}]`);
1031+
}}
1032+
waitableIdx = hostStream.getStreamEndWaitableIdx();
1033+
}} else {{
1034+
const cstate = {get_or_create_async_state_fn}(componentIdx);
1035+
if (!cstate) {{
1036+
throw new Error(`missing async state for component [${{componentIdx}}]`);
1037+
}}
1038+
1039+
const {{ writeEnd, readEnd }} = cstate.createStream({{
1040+
tableIdx: streamTableIdx,
1041+
elemMeta,
1042+
}});
1043+
1044+
const hostInjectFn = {gen_host_inject_fn}({{
1045+
readFn: {gen_read_fn_from_lowerable_stream_fn}(stream),
1046+
hostWriteEnd: writeEnd,
1047+
}});
1048+
readEnd.setHostInjectFn(hostInjectFn);
1049+
1050+
waitableIdx = readEnd.waitableIdx();
1051+
}}
1052+
}} else {{
1053+
throw new Error('object does not conform to supported stream interfaces');
10171054
}}
10181055
1019-
const readEnd = internalStream.readEnd();
1020-
const waitableIdx = readEnd.waitableIdx();
1021-
10221056
// Write the idx of the waitable to memory (a waiting async task or caller)
10231057
if (ctx.storagePtr) {{
1024-
new DataView(ctx.memory.buffer).setUint32(ctx.storagePtr, waitableIdx, true);
1058+
ctx.vals[0] = waitableIdx;
1059+
{lower_u32_fn}(ctx);
10251060
}}
10261061
1027-
// TODO: if we flat lower another way (host -> guest async) we need to actually
1028-
// modify the guests table's afresh, we can't just use the global rep!
1029-
// (can detect this by whether the external stream has a rep or not)
1030-
10311062
return waitableIdx;
10321063
}}
10331064
}}

crates/js-component-bindgen/src/intrinsics/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,23 @@ pub fn render_intrinsics(args: RenderIntrinsicsArgs) -> Source {
13751375
]);
13761376
}
13771377

1378+
if args
1379+
.intrinsics
1380+
.contains(&Intrinsic::Lower(LowerIntrinsic::LowerFlatStream))
1381+
{
1382+
args.intrinsics.extend([
1383+
&Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamMap),
1384+
&Intrinsic::AsyncStream(AsyncStreamIntrinsic::ExternalStreamClass),
1385+
&Intrinsic::AsyncStream(AsyncStreamIntrinsic::InternalStreamClass),
1386+
&Intrinsic::AsyncStream(AsyncStreamIntrinsic::IsStreamLowerableObject),
1387+
&Intrinsic::SymbolResourceRep,
1388+
&Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState),
1389+
&Intrinsic::AsyncStream(AsyncStreamIntrinsic::GenReadFnFromLowerableStream),
1390+
&Intrinsic::AsyncStream(AsyncStreamIntrinsic::GenHostInjectFn),
1391+
&Intrinsic::Lower(LowerIntrinsic::LowerFlatU32),
1392+
])
1393+
}
1394+
13781395
if args
13791396
.intrinsics
13801397
.contains(&Intrinsic::Lift(LiftIntrinsic::LiftFlatStringAny))

0 commit comments

Comments
 (0)