@@ -17,7 +17,7 @@ use crate::bundled::wado_bundled_libm_wasm;
1717use crate :: component_model:: { CmInstanceTypeGen , CmVariantCase , WasiFunctionInfo } ;
1818use crate :: hashmap:: { IndexMap , IndexSet } ;
1919use crate :: project:: Project ;
20- use crate :: wir:: { CanonicalIntrinsic , CmFuturePayload , CmScalarType , WirModule } ;
20+ use crate :: wir:: { CanonicalIntrinsic , CmFuturePayload , CmScalarType , CmStreamPayload , WirModule } ;
2121use wasm_encoder:: {
2222 Alias , CanonicalOption , ComponentBuilder , ComponentExportKind , ComponentOuterAliasKind ,
2323 ComponentValType , ExportKind , InstanceType , ModuleArg , PrimitiveValType , TypeBounds ,
@@ -32,14 +32,6 @@ pub fn build_component(project: &Project, core_module: &[u8], wir_module: &WirMo
3232 // Generate WASI imports dynamically from registry
3333 generate_wasi_imports ( & mut builder, & mut ctx, project) ;
3434
35- // Type: stream<u8> for stream intrinsics
36- let stream_u8_type = ctx. register_type ( "stream-u8" ) ;
37- {
38- let ( _, enc) = builder. ty ( Some ( "stream-u8" ) ) ;
39- enc. defined_type ( )
40- . stream ( Some ( ComponentValType :: Primitive ( PrimitiveValType :: U8 ) ) ) ;
41- }
42-
4335 // Type: result unit for run function (needed for task.return)
4436 let result_unit_type = ctx. register_type ( "result-unit" ) ;
4537 {
@@ -89,6 +81,49 @@ pub fn build_component(project: &Project, core_module: &[u8], wir_module: &WirMo
8981 let all_canonical_intrinsics: Vec < CanonicalIntrinsic > =
9082 wir_module. needed_canonicals . iter ( ) . cloned ( ) . collect ( ) ;
9183
84+ // Build stream types needed by canonical intrinsics.
85+ let stream_types: IndexMap < CmStreamPayload , u32 > = {
86+ let mut payloads: Vec < CmStreamPayload > = Vec :: new ( ) ;
87+ for intrinsic in & all_canonical_intrinsics {
88+ if let Some ( p) = intrinsic. stream_payload ( )
89+ && !payloads. contains ( & p)
90+ {
91+ payloads. push ( p) ;
92+ }
93+ }
94+ let mut map = IndexMap :: default ( ) ;
95+ for payload in payloads {
96+ let ( type_key, val_type) = match & payload {
97+ CmStreamPayload :: U8 => (
98+ "stream-u8" . to_string ( ) ,
99+ ComponentValType :: Primitive ( PrimitiveValType :: U8 ) ,
100+ ) ,
101+ CmStreamPayload :: Record ( name) => {
102+ // Alias the record type from the WASI interface that defines it.
103+ // WASI imports are already generated (generate_wasi_imports runs first),
104+ // so we can alias the exported type from the interface instance.
105+ let val = if let Some ( interface_name) = project
106+ . wasi_registry
107+ . find_interface_for_struct_cm_name ( name)
108+ {
109+ let inst_idx = ctx. instance_idx ( & interface_name) ;
110+ builder. alias_export ( inst_idx, name, ComponentExportKind :: Type ) ;
111+ let aliased_idx = ctx. register_type ( name) ;
112+ ComponentValType :: Type ( aliased_idx)
113+ } else {
114+ ComponentValType :: Primitive ( PrimitiveValType :: U8 )
115+ } ;
116+ ( format ! ( "stream-{name}" ) , val)
117+ }
118+ } ;
119+ ctx. register_type ( & type_key) ;
120+ let ( _, enc) = builder. ty ( Some ( & type_key) ) ;
121+ enc. defined_type ( ) . stream ( Some ( val_type) ) ;
122+ map. insert ( payload, ctx. type_idx ( & type_key) ) ;
123+ }
124+ map
125+ } ;
126+
92127 // HTTP response types for future<T> canonical intrinsics
93128 let needs_trailers_future = all_canonical_intrinsics
94129 . iter ( )
@@ -105,6 +140,19 @@ pub fn build_component(project: &Project, core_module: &[u8], wir_module: &WirMo
105140 } )
106141 . collect ( ) ;
107142
143+ // stream<u8> type is also needed by HTTP future types
144+ let stream_u8_type = stream_types
145+ . get ( & CmStreamPayload :: U8 )
146+ . copied ( )
147+ . unwrap_or_else ( || {
148+ // If no stream<u8> intrinsics are needed, define it anyway for HTTP
149+ ctx. register_type ( "stream-u8" ) ;
150+ let ( _, enc) = builder. ty ( Some ( "stream-u8" ) ) ;
151+ enc. defined_type ( )
152+ . stream ( Some ( ComponentValType :: Primitive ( PrimitiveValType :: U8 ) ) ) ;
153+ ctx. type_idx ( "stream-u8" )
154+ } ) ;
155+
108156 let ( trailers_future_type, transmission_future_types) = if needs_trailers_future {
109157 let ( t, http_ft) = build_future_intrinsic_types ( & mut builder, & mut ctx, stream_u8_type) ;
110158 let mut map: IndexMap < String , u32 > = IndexMap :: default ( ) ;
@@ -138,7 +186,7 @@ pub fn build_component(project: &Project, core_module: &[u8], wir_module: &WirMo
138186 & mut builder,
139187 & mut ctx,
140188 & all_canonical_intrinsics,
141- stream_u8_type ,
189+ & stream_types ,
142190 result_unit_type,
143191 trailers_future_type,
144192 & transmission_future_types,
@@ -342,11 +390,21 @@ fn emit_cm_val_type(
342390) -> ComponentValType {
343391 match ty {
344392 Type :: Generic ( g) if g. name == "Stream" => {
345- // Only u8 streams are supported in WASI P3
346- instance_type
347- . ty ( )
348- . defined_type ( )
349- . stream ( Some ( ComponentValType :: Primitive ( PrimitiveValType :: U8 ) ) ) ;
393+ let element = g. args . first ( ) . map ( |inner| {
394+ emit_cm_val_type (
395+ inner,
396+ instance_type,
397+ local_type_idx,
398+ error_code_idx,
399+ has_local_error_code,
400+ enum_export_indices,
401+ own_resource_type_indices,
402+ shared_type_gen. as_deref_mut ( ) ,
403+ project,
404+ ctx,
405+ )
406+ } ) ;
407+ instance_type. ty ( ) . defined_type ( ) . stream ( element) ;
350408 let idx = * local_type_idx;
351409 * local_type_idx += 1 ;
352410 ComponentValType :: Type ( idx)
@@ -443,8 +501,18 @@ fn emit_cm_val_type(
443501 ComponentValType :: Type ( idx)
444502 }
445503 Type :: Generic ( g) if g. name == "Option" && !g. args . is_empty ( ) => {
446- let element_val_type =
447- type_to_cm_primitive_with_resources ( & g. args [ 0 ] , own_resource_type_indices) ;
504+ let element_val_type = emit_cm_val_type (
505+ & g. args [ 0 ] ,
506+ instance_type,
507+ local_type_idx,
508+ error_code_idx,
509+ has_local_error_code,
510+ enum_export_indices,
511+ own_resource_type_indices,
512+ shared_type_gen,
513+ project,
514+ ctx,
515+ ) ;
448516 instance_type. ty ( ) . defined_type ( ) . option ( element_val_type) ;
449517 let idx = * local_type_idx;
450518 * local_type_idx += 1 ;
@@ -459,6 +527,8 @@ fn emit_cm_val_type(
459527 has_local_error_code,
460528 enum_export_indices,
461529 own_resource_type_indices,
530+ shared_type_gen. as_deref_mut ( ) ,
531+ project,
462532 ctx,
463533 ) ;
464534 instance_type. ty ( ) . defined_type ( ) . tuple ( tuple_types) ;
@@ -475,6 +545,8 @@ fn emit_cm_val_type(
475545 has_local_error_code,
476546 enum_export_indices,
477547 own_resource_type_indices,
548+ shared_type_gen. as_deref_mut ( ) ,
549+ project,
478550 ctx,
479551 ) ;
480552 instance_type. ty ( ) . defined_type ( ) . tuple ( tuple_types) ;
@@ -489,6 +561,22 @@ fn emit_cm_val_type(
489561 {
490562 return ComponentValType :: Type ( idx) ;
491563 }
564+ // Complex types (e.g. WASI records like Instant) use shared type gen
565+ if let ( Some ( type_gen) , Some ( proj) ) = ( shared_type_gen, project) {
566+ type_gen. set_next_idx ( * local_type_idx) ;
567+ let resource_exports: IndexMap < & str , u32 > = own_resource_type_indices
568+ . iter ( )
569+ . map ( |( k, & v) | ( k. as_str ( ) , v) )
570+ . collect ( ) ;
571+ let val = type_gen. ast_type_to_cm (
572+ ty,
573+ instance_type,
574+ proj. wasi_registry ,
575+ & resource_exports,
576+ ) ;
577+ * local_type_idx = type_gen. next_idx ( ) ;
578+ return val;
579+ }
492580 type_to_cm_primitive_with_resources ( ty, own_resource_type_indices)
493581 }
494582 }
@@ -528,6 +616,8 @@ fn build_cm_tuple_types(
528616 has_local_error_code : bool ,
529617 enum_export_indices : & IndexMap < String , u32 > ,
530618 own_resource_type_indices : & IndexMap < String , u32 > ,
619+ mut shared_type_gen : Option < & mut CmInstanceTypeGen > ,
620+ project : Option < & Project > ,
531621 ctx : & mut ComponentModelContext ,
532622) -> Vec < ComponentValType > {
533623 elems
@@ -541,8 +631,8 @@ fn build_cm_tuple_types(
541631 has_local_error_code,
542632 enum_export_indices,
543633 own_resource_type_indices,
544- None ,
545- None ,
634+ shared_type_gen . as_deref_mut ( ) ,
635+ project ,
546636 ctx,
547637 )
548638 } )
@@ -864,7 +954,7 @@ fn emit_canonical_intrinsics(
864954 builder : & mut ComponentBuilder ,
865955 ctx : & mut ComponentModelContext ,
866956 canonical_intrinsics : & [ CanonicalIntrinsic ] ,
867- stream_u8_type : u32 ,
957+ stream_types : & IndexMap < CmStreamPayload , u32 > ,
868958 result_unit_type : u32 ,
869959 trailers_future_type : u32 ,
870960 transmission_future_types : & IndexMap < String , u32 > ,
@@ -874,38 +964,41 @@ fn emit_canonical_intrinsics(
874964 ctx. register_core_func ( & intrinsic. import_name ( ) ) ;
875965
876966 match intrinsic {
877- CanonicalIntrinsic :: StreamNew => {
878- builder. stream_new ( stream_u8_type) ;
967+ CanonicalIntrinsic :: StreamNew ( payload) => {
968+ let st = stream_types[ payload] ;
969+ builder. stream_new ( st) ;
879970 }
880- CanonicalIntrinsic :: StreamWrite => {
971+ CanonicalIntrinsic :: StreamWrite ( payload) => {
972+ let st = stream_types[ payload] ;
881973 builder. stream_write (
882- stream_u8_type ,
974+ st ,
883975 [
884976 CanonicalOption :: Memory ( ctx. memory_idx ( ) ) ,
885977 CanonicalOption :: Realloc ( ctx. core_func_idx ( "realloc" ) ) ,
886978 ] ,
887979 ) ;
888980 }
889- CanonicalIntrinsic :: StreamRead => {
981+ CanonicalIntrinsic :: StreamRead ( payload) => {
982+ let st = stream_types[ payload] ;
890983 builder. stream_read (
891- stream_u8_type ,
984+ st ,
892985 [
893986 CanonicalOption :: Memory ( ctx. memory_idx ( ) ) ,
894987 CanonicalOption :: Realloc ( ctx. core_func_idx ( "realloc" ) ) ,
895988 ] ,
896989 ) ;
897990 }
898- CanonicalIntrinsic :: StreamDropWritable => {
899- builder. stream_drop_writable ( stream_u8_type ) ;
991+ CanonicalIntrinsic :: StreamDropWritable ( payload ) => {
992+ builder. stream_drop_writable ( stream_types [ payload ] ) ;
900993 }
901- CanonicalIntrinsic :: StreamDropReadable => {
902- builder. stream_drop_readable ( stream_u8_type ) ;
994+ CanonicalIntrinsic :: StreamDropReadable ( payload ) => {
995+ builder. stream_drop_readable ( stream_types [ payload ] ) ;
903996 }
904- CanonicalIntrinsic :: StreamCancelRead => {
905- builder. stream_cancel_read ( stream_u8_type , false ) ;
997+ CanonicalIntrinsic :: StreamCancelRead ( payload ) => {
998+ builder. stream_cancel_read ( stream_types [ payload ] , false ) ;
906999 }
907- CanonicalIntrinsic :: StreamCancelWrite => {
908- builder. stream_cancel_write ( stream_u8_type , false ) ;
1000+ CanonicalIntrinsic :: StreamCancelWrite ( payload ) => {
1001+ builder. stream_cancel_write ( stream_types [ payload ] , false ) ;
9091002 }
9101003 CanonicalIntrinsic :: FutureNew ( payload) => {
9111004 let ft = resolve_future_type (
@@ -1407,25 +1500,35 @@ fn generate_wasi_imports(
14071500 ( wado_name. to_string ( ) , ( cm_name. to_string ( ) , cases. to_vec ( ) ) )
14081501 } )
14091502 . collect ( ) ;
1503+ // Shared CmInstanceTypeGen for complex types across variant payloads and functions.
1504+ // Created early so variant payload types (e.g. Instant in NewTimestamp) are cached
1505+ // and reused when the same types appear in function signatures.
1506+ let mut shared_type_gen = CmInstanceTypeGen :: new ( local_type_idx) ;
1507+ for ( name, & idx) in & enum_export_indices {
1508+ shared_type_gen. register_existing ( & format ! ( "enum:{name}" ) , idx) ;
1509+ }
14101510 for variant_name in & needed_variants {
14111511 if let Some ( ( _, cases) ) = interface_variants. get ( variant_name) {
14121512 // Build CM variant cases: (kebab-name, optional payload type)
14131513 let cm_cases: Vec < ( & str , Option < ComponentValType > ) > = cases
14141514 . iter ( )
14151515 . map ( |c| {
14161516 let payload = c. payload . as_ref ( ) . map ( |ty| {
1417- emit_cm_val_type (
1517+ shared_type_gen. set_next_idx ( local_type_idx) ;
1518+ let val = emit_cm_val_type (
14181519 ty,
14191520 & mut instance_type,
14201521 & mut local_type_idx,
14211522 None ,
14221523 has_local_error_code,
14231524 & enum_export_indices,
14241525 & own_resource_type_indices,
1425- None ,
1426- None ,
1526+ Some ( & mut shared_type_gen ) ,
1527+ Some ( project ) ,
14271528 ctx,
1428- )
1529+ ) ;
1530+ local_type_idx = shared_type_gen. next_idx ( ) . max ( local_type_idx) ;
1531+ val
14291532 } ) ;
14301533 ( c. cm_name . as_str ( ) , payload)
14311534 } )
@@ -1476,17 +1579,11 @@ fn generate_wasi_imports(
14761579
14771580 let mut deferred_func_exports: Vec < ( String , u32 ) > = Vec :: new ( ) ;
14781581
1479- // Shared CmInstanceTypeGen for complex ok types across all functions.
1480- // Reusing one instance avoids duplicate type definitions and exports.
1481- let mut shared_type_gen = CmInstanceTypeGen :: new ( local_type_idx) ;
1482- for ( name, & idx) in & enum_export_indices {
1483- shared_type_gen. register_existing ( & format ! ( "enum:{name}" ) , idx) ;
1484- }
1582+ // Register flags and variant export indices into shared_type_gen
14851583 for ( name, & idx) in & flags_export_indices {
14861584 shared_type_gen. register_existing ( & format ! ( "flags:{name}" ) , idx) ;
14871585 }
14881586 for ( name, & idx) in & variant_export_indices {
1489- // shared_type_gen uses CM kebab-case names for cache keys
14901587 if let Some ( ( variant_cm_name, _) ) = interface_variants. get ( name) {
14911588 shared_type_gen. register_existing ( & format ! ( "variant:{variant_cm_name}" ) , idx) ;
14921589 }
0 commit comments