@@ -4,8 +4,9 @@ use std::mem;
44
55use heck:: { ToLowerCamelCase , ToUpperCamelCase } ;
66use wasmtime_environ:: component:: {
7- CanonicalOptions , InterfaceType , ResourceIndex , TypeComponentLocalErrorContextTableIndex ,
8- TypeFutureTableIndex , TypeResourceTableIndex , TypeStreamTableIndex ,
7+ CanonicalOptions , CanonicalOptionsDataModel , InterfaceType , LinearMemoryOptions , ResourceIndex ,
8+ TypeComponentLocalErrorContextTableIndex , TypeFutureTableIndex , TypeResourceTableIndex ,
9+ TypeStreamTableIndex ,
910} ;
1011use wit_bindgen_core:: abi:: { Bindgen , Bitcast , Instruction } ;
1112use wit_component:: StringEncoding ;
@@ -2685,19 +2686,172 @@ impl Bindgen for FunctionBindgen<'_> {
26852686 results. push ( item. clone ( ) ) ;
26862687 }
26872688
2688- Instruction :: FutureLower { .. } => {
2689- // TODO: convert this return of the lifted Future:
2690- //
2691- // ```
2692- // return BigInt(writeEndWaitableIdx) << 32n | BigInt(readEndWaitableIdx);
2693- // ```
2694- //
2695- // Into a component-local Future instance
2696- //
2689+ Instruction :: FutureLower { ty, .. } => {
2690+ let debug_log_fn = self . intrinsic ( Intrinsic :: DebugLog ) ;
2691+ let get_or_create_async_state_fn = self . intrinsic ( Intrinsic :: Component (
2692+ ComponentIntrinsic :: GetOrCreateAsyncState ,
2693+ ) ) ;
2694+ let gen_future_host_inject_fn = self . intrinsic ( Intrinsic :: AsyncFuture (
2695+ AsyncFutureIntrinsic :: GenFutureHostInjectFn ,
2696+ ) ) ;
2697+ let is_future_lowerable_object_fn = self . intrinsic ( Intrinsic :: AsyncFuture (
2698+ AsyncFutureIntrinsic :: IsFutureLowerableObject ,
2699+ ) ) ;
2700+
2701+ let component_idx = self . canon_opts . instance . as_u32 ( ) ;
2702+
26972703 let future_arg = operands
26982704 . first ( )
26992705 . expect ( "unexpectedly missing ErrorContextLower arg" ) ;
2700- results. push ( future_arg. clone ( ) ) ;
2706+
2707+ // Build the lowering function for the type produced by the future
2708+ let type_id = & crate :: dealias ( self . resolve , * ty) ;
2709+ let ResourceTable {
2710+ imported : true ,
2711+ data :
2712+ ResourceData :: Guest {
2713+ extra :
2714+ Some ( ResourceExtraData :: Future {
2715+ table_idx : future_table_idx_ty,
2716+ elem_ty,
2717+ } ) ,
2718+ ..
2719+ } ,
2720+ } = self
2721+ . resource_map
2722+ . get ( type_id)
2723+ . expect ( "missing resource mapping for future lower" )
2724+ else {
2725+ unreachable ! ( "invalid resource table observed during future lower" ) ;
2726+ } ;
2727+ let future_table_idx = future_table_idx_ty. as_u32 ( ) ;
2728+
2729+ // Generate payload metadata ('elemMeta')
2730+ let (
2731+ payload_type_name_js,
2732+ lift_fn_js,
2733+ lower_fn_js,
2734+ payload_is_none,
2735+ payload_is_numeric,
2736+ payload_is_borrow,
2737+ payload_is_async_value,
2738+ payload_size32_js,
2739+ payload_align32_js,
2740+ payload_flat_count_js,
2741+ ) = match elem_ty {
2742+ Some ( PayloadTypeMetadata {
2743+ ty : _,
2744+ iface_ty,
2745+ lift_js_expr,
2746+ lower_js_expr,
2747+ size32,
2748+ align32,
2749+ flat_count,
2750+ } ) => (
2751+ format ! ( "'{iface_ty:?}'" ) ,
2752+ lift_js_expr. as_str ( ) ,
2753+ lower_js_expr. as_str ( ) ,
2754+ "false" ,
2755+ format ! (
2756+ "{}" ,
2757+ matches!(
2758+ iface_ty,
2759+ InterfaceType :: U8
2760+ | InterfaceType :: U16
2761+ | InterfaceType :: U32
2762+ | InterfaceType :: U64
2763+ | InterfaceType :: S8
2764+ | InterfaceType :: S16
2765+ | InterfaceType :: S32
2766+ | InterfaceType :: S64
2767+ | InterfaceType :: Float32
2768+ | InterfaceType :: Float64
2769+ )
2770+ ) ,
2771+ format ! ( "{}" , matches!( iface_ty, InterfaceType :: Borrow ( _) ) ) ,
2772+ format ! (
2773+ "{}" ,
2774+ matches!(
2775+ iface_ty,
2776+ InterfaceType :: Stream ( _) | InterfaceType :: Future ( _)
2777+ )
2778+ ) ,
2779+ size32. to_string ( ) ,
2780+ align32. to_string ( ) ,
2781+ flat_count. unwrap_or ( 0 ) . to_string ( ) ,
2782+ ) ,
2783+ None => (
2784+ "null" . into ( ) ,
2785+ "() => {{ throw new Error('no lift fn'); }}" ,
2786+ "() => {{ throw new Error('no lower fn'); }}" ,
2787+ "true" ,
2788+ "false" . into ( ) ,
2789+ "false" . into ( ) ,
2790+ "false" . into ( ) ,
2791+ "null" . into ( ) ,
2792+ "null" . into ( ) ,
2793+ "0" . into ( ) ,
2794+ ) ,
2795+ } ;
2796+
2797+ // Retrieve the realloc fn if present, in case lowering fns need to allocate
2798+ //
2799+ // The realloc fn is saved on the element metadata which is passed through to
2800+ // stream end and underlying buffer
2801+ let get_realloc_fn_js = match self . canon_opts . data_model {
2802+ CanonicalOptionsDataModel :: LinearMemory ( LinearMemoryOptions {
2803+ realloc : Some ( realloc_idx) ,
2804+ ..
2805+ } ) => format ! ( "() => realloc{}" , realloc_idx. as_u32( ) ) ,
2806+ _ => "undefined" . into ( ) ,
2807+ } ;
2808+
2809+ let tmp = self . tmp ( ) ;
2810+ let lowered_future_waitable_idx = format ! ( "futureWaitableIdx{tmp}" ) ;
2811+ uwriteln ! (
2812+ self . src,
2813+ r#"
2814+ if (!{is_future_lowerable_object_fn}({future_arg})) {{
2815+ {debug_log_fn}('[Instruction::FutureLower] object is not a Promise/Thenable', {{ {future_arg} }});
2816+ throw new Error('unrecognized future object (not Promise/Thenable)');
2817+ }}
2818+
2819+ const cstate{tmp} = {get_or_create_async_state_fn}({component_idx});
2820+ if (!cstate{tmp}) {{ throw new Error(`missing component state for component [{component_idx}]`); }}
2821+
2822+ // TODO(feat): facilitate non utf8 string encoding for lowered futures
2823+ const stringEncoding = 'utf8';
2824+
2825+ const {{ writeEnd: hostWriteEnd{tmp}, readEnd: readEnd{tmp} }} = cstate{tmp}.createFuture({{
2826+ tableIdx: {future_table_idx},
2827+ elemMeta: {{
2828+ liftFn: {lift_fn_js},
2829+ lowerFn: {lower_fn_js},
2830+ payloadTypeName: {payload_type_name_js},
2831+ isNone: {payload_is_none},
2832+ isNumeric: {payload_is_numeric},
2833+ isBorrowed: {payload_is_borrow},
2834+ isAsyncValue: {payload_is_async_value},
2835+ flatCount: {payload_flat_count_js},
2836+ align32: {payload_align32_js},
2837+ size32: {payload_size32_js},
2838+ stringEncoding,
2839+ getReallocFn: {get_realloc_fn_js},
2840+ }},
2841+ }});
2842+
2843+ const hostInjectFn = {gen_future_host_inject_fn}({{
2844+ promise: {future_arg},
2845+ stringEncoding,
2846+ hostWriteEnd: hostWriteEnd{tmp},
2847+ }});
2848+ readEnd{tmp}.setHostInjectFn(hostInjectFn);
2849+
2850+ const {lowered_future_waitable_idx} = readEnd{tmp}.waitableIdx();
2851+ "#
2852+ ) ;
2853+
2854+ results. push ( lowered_future_waitable_idx) ;
27012855 }
27022856
27032857 Instruction :: FutureLift { payload, ty } => {
@@ -2802,9 +2956,12 @@ impl Bindgen for FunctionBindgen<'_> {
28022956 ComponentIntrinsic :: GetOrCreateAsyncState ,
28032957 ) ) ;
28042958 let gen_host_inject_fn = self . intrinsic ( Intrinsic :: AsyncStream (
2805- AsyncStreamIntrinsic :: GenHostInjectFn ,
2959+ AsyncStreamIntrinsic :: GenStreamHostInjectFn ,
28062960 ) ) ;
28072961
2962+ // TODO(???): A component could end up receiving a stream that it outputted,
2963+ // and the below would fail (imported: false)?
2964+
28082965 // Build the lowering function for the type produced by the stream
28092966 let type_id = & crate :: dealias ( self . resolve , * ty) ;
28102967 let ResourceTable {
@@ -2896,6 +3053,18 @@ impl Bindgen for FunctionBindgen<'_> {
28963053 ) ,
28973054 } ;
28983055
3056+ // Retrieve the realloc fn if present, in case lowering fns need to allocate
3057+ //
3058+ // The realloc fn is saved on the element metadata which is passed through to
3059+ // stream end and underlying buffer
3060+ let get_realloc_fn_js = match self . canon_opts . data_model {
3061+ CanonicalOptionsDataModel :: LinearMemory ( LinearMemoryOptions {
3062+ realloc : Some ( realloc_idx) ,
3063+ ..
3064+ } ) => format ! ( "() => realloc{}" , realloc_idx. as_u32( ) ) ,
3065+ _ => "undefined" . into ( ) ,
3066+ } ;
3067+
28993068 let tmp = self . tmp ( ) ;
29003069 let lowered_stream_waitable_idx = format ! ( "streamWaitableIdx{tmp}" ) ;
29013070 uwriteln ! (
@@ -2926,6 +3095,7 @@ impl Bindgen for FunctionBindgen<'_> {
29263095 size32: {payload_size32_js},
29273096 // TODO(feat): facilitate non utf8 string encoding for lowered streams
29283097 stringEncoding: 'utf8',
3098+ geReallocFn: {get_realloc_fn_js},
29293099 }},
29303100 }});
29313101
@@ -2952,6 +3122,7 @@ impl Bindgen for FunctionBindgen<'_> {
29523122 const {lowered_stream_waitable_idx} = readEnd{tmp}.waitableIdx();
29533123 "#
29543124 ) ;
3125+
29553126 results. push ( lowered_stream_waitable_idx) ;
29563127 }
29573128
0 commit comments