diff --git a/crates/js-component-bindgen/src/intrinsics/lift.rs b/crates/js-component-bindgen/src/intrinsics/lift.rs index 6a1387820..5869ba249 100644 --- a/crates/js-component-bindgen/src/intrinsics/lift.rs +++ b/crates/js-component-bindgen/src/intrinsics/lift.rs @@ -562,6 +562,13 @@ impl LiftIntrinsic { if (ctx.params.length === 0) {{ throw new Error('expected at least one single f32 argument'); }} val = ctx.params[0]; ctx.params = ctx.params.slice(1); + + if (ctx.inVariant) {{ + const dv = new DataView(new ArrayBuffer(4)); + dv.setInt32(0, val); + val = dv.getFloat32(0); + }} + return [val, ctx]; }} @@ -583,29 +590,41 @@ impl LiftIntrinsic { Self::LiftFlatFloat64 => { let debug_log_fn = Intrinsic::DebugLog.name(); let lift_flat_f64_fn = self.name(); - output.push_str(&format!(" - function {lift_flat_f64_fn}(ctx) {{ - {debug_log_fn}('[{lift_flat_f64_fn}()] args', {{ ctx }}); - let val; - - if (ctx.useDirectParams) {{ - if (ctx.params.length === 0) {{ throw new Error('expected at least one single f64 argument'); }} - val = ctx.params[0]; - ctx.params = ctx.params.slice(1); - return [val, ctx]; - }} - - if (ctx.storageLen !== undefined && ctx.storageLen < 8) {{ - throw new Error(`insufficient storage ([${{ctx.storageLen}}] bytes) for lift (f64 requires 8 bytes)`); - }} - - val = new DataView(ctx.memory.buffer).getFloat64(ctx.storagePtr, true); - ctx.storagePtr += 8; - if (ctx.storageLen !== undefined) {{ ctx.storageLen -= 8; }} - - return [val, ctx]; - }} - ")); + uwriteln!( + output, + r#" + function {lift_flat_f64_fn}(ctx) {{ + {debug_log_fn}('[{lift_flat_f64_fn}()] args', {{ ctx }}); + let val; + + if (ctx.useDirectParams) {{ + if (ctx.params.length === 0) {{ + throw new Error('expected at least one single f64 argument'); + }} + val = ctx.params[0]; + ctx.params = ctx.params.slice(1); + + if (ctx.inVariant) {{ + const dv = new DataView(new ArrayBuffer(8)); + dv.setBigInt64(0, val); + val = dv.getFloat64(0); + }} + + return [val, ctx]; + }} + + if (ctx.storageLen !== undefined && ctx.storageLen < 8) {{ + throw new Error(`insufficient storage ([${{ctx.storageLen}}] bytes) for lift (f64 requires 8 bytes)`); + }} + + val = new DataView(ctx.memory.buffer).getFloat64(ctx.storagePtr, true); + ctx.storagePtr += 8; + if (ctx.storageLen !== undefined) {{ ctx.storageLen -= 8; }} + + return [val, ctx]; + }} + "# + ); } Self::LiftFlatChar => { @@ -667,8 +686,9 @@ impl LiftIntrinsic { if (ctx.useDirectParams) {{ if (ctx.params.length < 2) {{ throw new Error('expected at least two u32 arguments'); }} - const offset = ctx.params[0]; - if (!Number.isSafeInteger(offset)) {{ throw new Error('invalid offset'); }} + let offset = ctx.params[0]; + if (typeof offset === 'bigint') {{ offset = Number(offset); }} + if (!Number.isSafeInteger(offset)) {{ throw new Error('invalid offset'); }} const len = ctx.params[1]; if (!Number.isSafeInteger(len)) {{ throw new Error('invalid len'); }} val = {decoder}.decode(new DataView(ctx.memory.buffer, offset, len)); @@ -705,6 +725,7 @@ impl LiftIntrinsic { if (ctx.useDirectParams) {{ if (ctx.params.length < 2) {{ throw new Error('expected at least two u32 arguments'); }} const offset = ctx.params[0]; + if (typeof offset === 'bigint') {{ offset = Number(offset); }} if (!Number.isSafeInteger(offset)) {{ throw new Error('invalid offset'); }} const len = ctx.params[1]; if (!Number.isSafeInteger(len)) {{ throw new Error('invalid len'); }} @@ -790,18 +811,33 @@ impl LiftIntrinsic { let lift_u8 = Self::LiftFlatU8.name(); let lift_u16 = Self::LiftFlatU16.name(); let lift_u32 = Self::LiftFlatU32.name(); + let lift_f64 = Self::LiftFlatFloat64.name(); + output.push_str(&format!(r#" - function {lift_flat_variant_fn}(casesAndLiftFns) {{ + function {lift_flat_variant_fn}(meta) {{ + const {{ + caseMetas, + variantSize32, + variantAlign32, + variantPayloadOffset32, + variantFlatCount, + isEnum, + }} = meta; + return function {lift_flat_variant_fn}Inner(ctx) {{ {debug_log_fn}('[{lift_flat_variant_fn}()] args', {{ ctx }}); - const origUseParams = ctx.useDirectParams; + // If we're in the process of lifting a variant, we note + // we are during any lifting that happens (e.g. to accomodate f32/f64 mechanics) + const wasInVariant = ctx.inVariant; + ctx.inVariant = true; + let caseIdx; let liftRes; const originalPtr = ctx.storagePtr; - const numCases = casesAndLiftFns.length; - if (casesAndLiftFns.length < 256) {{ + const numCases = caseMetas.length; + if (caseMetas.length < 256) {{ liftRes = {lift_u8}(ctx); }} else if (numCases >= 256 && numCases < 65536) {{ liftRes = {lift_u16}(ctx); @@ -813,11 +849,20 @@ impl LiftIntrinsic { caseIdx = liftRes[0]; ctx = liftRes[1]; - const [ tag, liftFn, size32, align32, payloadOffset32, caseFlatCount, variantFlatCount ] = casesAndLiftFns[caseIdx]; - if (payloadOffset32 === undefined) {{ throw new Error('unexpectedly missing payload offset'); }} + const [ + tag, + liftFn, + caseSize32, + caseAlign32, + caseFlatCount, + ] = caseMetas[caseIdx]; + + if (variantPayloadOffset32 === undefined) {{ + throw new Error('unexpectedly missing payload offset'); + }} if (originalPtr !== undefined) {{ - ctx.storagePtr = originalPtr + payloadOffset32; + ctx.storagePtr = originalPtr + variantPayloadOffset32; }} let val; @@ -826,28 +871,32 @@ impl LiftIntrinsic { // NOTE: here we need to move past the entire object in memory // despite moving to the payload which we now know is missing/unnecessary if (originalPtr !== undefined) {{ - ctx.storagePtr = originalPtr + size32; + ctx.storagePtr = originalPtr + variantSize32; }} }} else {{ + if (ctx.useDirectParams && ctx.params && liftFn !== {lift_f64} && typeof ctx.params[0] === 'bigint') {{ + if (ctx.params[0] > BigInt(Number.MAX_SAFE_INTEGER)) {{ + throw new Error(`invalid value, reinterpreted i32/f32 too large: [${{ctx.params[0]}}]`); + }} + ctx.params[0] = Number(ctx.params[0]); + }} + const [newVal, newCtx] = liftFn(ctx); val = {{ tag, val: newVal }}; ctx = newCtx; - - // NOTE: Padding can be left over after doing the lift if it was less than - // space left for the payload normally. - if (originalPtr !== undefined) {{ - ctx.storagePtr = Math.max(ctx.storagePtr, originalPtr + size32); - }} }} if (origUseParams) {{ - if (caseFlatCount === undefined || variantFlatCount === undefined) {{ - throw new Error('variant flat count metadata is missing'); - }} - if (caseFlatCount === null || variantFlatCount === null) {{ + if (variantFlatCount === undefined || variantFlatCount === null) {{ + {debug_log_fn}('[{lift_flat_variant_fn}()] variant with unknown flat count', {{ ctx, meta }}); throw new Error('cannot lift variant with unknown flat count'); }} - const remainingPayloadParams = variantFlatCount - 1 - caseFlatCount; + if (caseFlatCount === undefined || caseFlatCount === null) {{ + {debug_log_fn}('[{lift_flat_variant_fn}()] case with unknown flat count', {{ ctx, meta, case: meta.caseMetas[caseIdx] }}); + throw new Error('cannot lift case with unknown flat count'); + }} + // NOTE: enums can be tightly packed and do not have a descriminant + const remainingPayloadParams = variantFlatCount - caseFlatCount - (isEnum ? 0 : 1); if (remainingPayloadParams < 0) {{ throw new Error(`invalid variant flat count metadata`); }} @@ -858,10 +907,12 @@ impl LiftIntrinsic { }} if (ctx.storagePtr !== undefined) {{ - const rem = ctx.storagePtr % align32; - if (rem !== 0) {{ ctx.storagePtr += align32 - rem; }} + const rem = ctx.storagePtr % variantAlign32; + if (rem !== 0) {{ ctx.storagePtr += variantAlign32 - rem; }} }} + ctx.inVariant = wasInVariant; + return [val, ctx]; }} }} @@ -1026,10 +1077,12 @@ impl LiftIntrinsic { let lift_flat_enum_fn = self.name(); output.push_str(&format!( r#" - function {lift_flat_enum_fn}(casesAndLiftFns) {{ + function {lift_flat_enum_fn}(meta) {{ + meta.isEnum = true; + const f = {lift_variant}(meta); return function {lift_flat_enum_fn}Inner(ctx) {{ {debug_log_fn}('[{lift_flat_enum_fn}()] args', {{ ctx }}); - const res = {lift_variant}(casesAndLiftFns)(ctx); + const res = f(ctx); res[0] = res[0].tag; return res; }} @@ -1044,10 +1097,11 @@ impl LiftIntrinsic { let lift_flat_option_fn = self.name(); output.push_str(&format!( r#" - function {lift_flat_option_fn}(casesAndLiftFns) {{ + function {lift_flat_option_fn}(meta) {{ + const f = {lift_variant}(meta); return function {lift_flat_option_fn}Inner(ctx) {{ {debug_log_fn}('[{lift_flat_option_fn}()] args', {{ ctx }}); - return {lift_variant}(casesAndLiftFns)(ctx); + return f(ctx); }} }} "# @@ -1060,10 +1114,11 @@ impl LiftIntrinsic { let lift_flat_result_fn = self.name(); output.push_str(&format!( r#" - function {lift_flat_result_fn}(casesAndLiftFns) {{ + function {lift_flat_result_fn}(meta) {{ + const f = {lift_variant}(meta); return function {lift_flat_result_fn}Inner(ctx) {{ {debug_log_fn}('[{lift_flat_result_fn}()] args', {{ ctx }}); - return {lift_variant}(casesAndLiftFns)(ctx); + return f(ctx); }} }} "# diff --git a/crates/js-component-bindgen/src/intrinsics/lower.rs b/crates/js-component-bindgen/src/intrinsics/lower.rs index 31caf0dac..e7f0eb235 100644 --- a/crates/js-component-bindgen/src/intrinsics/lower.rs +++ b/crates/js-component-bindgen/src/intrinsics/lower.rs @@ -648,9 +648,11 @@ impl LowerIntrinsic { let lower_u32_fn = Self::LowerFlatU32.name(); output.push_str(&format!(r#" - function {lower_flat_variant_fn}(lowerMetas) {{ + function {lower_flat_variant_fn}(meta) {{ + const {{ variantSize32, variantAlign32, variantPayloadOffset32, caseMetas }} = meta; + let caseLookup = {{}}; - for (const [idx, meta] of lowerMetas.entries()) {{ + for (const [idx, meta] of caseMetas.entries()) {{ let tag = meta[0]; caseLookup[tag] = {{ discriminant: idx, meta }}; }} @@ -664,30 +666,30 @@ impl LowerIntrinsic { throw new Error(`missing tag [${{tag}}] (valid tags: ${{Object.keys(caseLookup)}})`); }} - const [ _tag, lowerFn, size32, align32, payloadOffset32 ] = variantCase.meta; + const [ _tag, lowerFn, caseSize32, caseAlign32, caseFlatCount ] = variantCase.meta; const originalPtr = ctx.storagePtr; ctx.vals = [variantCase.discriminant]; let discLowerRes; - if (lowerMetas.length < 256) {{ + if (caseMetas.length < 256) {{ discLowerRes = {lower_u8_fn}(ctx); - }} else if (lowerMetas.length >= 256 && lowerMetas.length < 65536) {{ + }} else if (caseMetas.length >= 256 && caseMetas.length < 65536) {{ discLowerRes = {lower_u16_fn}(ctx); - }} else if (lowerMetas.length >= 65536 && lowerMetas.length < 4_294_967_296) {{ + }} else if (caseMetas.length >= 65536 && caseMetas.length < 4_294_967_296) {{ discLowerRes = {lower_u32_fn}(ctx); }} else {{ - throw new Error(`unsupported number of cases [${{lowerMetas.length}}]`); + throw new Error(`unsupported number of cases [${{caseMetas.length}}]`); }} - const payloadOffsetPtr = originalPtr + payloadOffset32; + const payloadOffsetPtr = originalPtr + variantPayloadOffset32; ctx.storagePtr = payloadOffsetPtr; ctx.vals = [val]; if (lowerFn) {{ lowerFn(ctx); }} - ctx.storagePtr = Math.max(ctx.storagePtr, originalPtr + size32); + ctx.storagePtr = Math.max(ctx.storagePtr, originalPtr + variantSize32); - const rem = ctx.storagePtr % align32; - if (rem !== 0) {{ ctx.storagePtr += align32 - rem; }} + const rem = ctx.storagePtr % variantAlign32; + if (rem !== 0) {{ ctx.storagePtr += varianttAlign32 - rem; }} }} }} "#)); @@ -883,7 +885,8 @@ impl LowerIntrinsic { output.push_str(&format!( r#" - function {lower_flat_enum_fn}(lowerMetas) {{ + function {lower_flat_enum_fn}(meta) {{ + const f = {lower_variant_fn}(meta); return function {lower_flat_enum_fn}Inner(ctx) {{ {debug_log_fn}('[{lower_flat_enum_fn}()] args', {{ ctx }}); @@ -895,7 +898,7 @@ impl LowerIntrinsic { ctx.vals[0] = {{ tag: v }}; }} - {lower_variant_fn}(lowerMetas)(ctx); + f(ctx); }} }} "# @@ -909,7 +912,8 @@ impl LowerIntrinsic { output.push_str(&format!( " - function {lower_flat_option_fn}(lowerMetas) {{ + function {lower_flat_option_fn}(meta) {{ + const f = {lower_variant_fn}(meta); return function {lower_flat_option_fn}Inner(ctx) {{ {debug_log_fn}('[{lower_flat_option_fn}()] args', {{ ctx }}); @@ -927,7 +931,7 @@ impl LowerIntrinsic { }} }} - {lower_variant_fn}(lowerMetas)(ctx); + f(ctx); }} }} " @@ -938,11 +942,13 @@ impl LowerIntrinsic { let debug_log_fn = Intrinsic::DebugLog.name(); let lower_flat_result_fn = self.name(); let lower_variant_fn = Self::LowerFlatVariant.name(); + output.push_str(&format!( r#" - function {lower_flat_result_fn}(lowerMetas) {{ + function {lower_flat_result_fn}(meta) {{ + const f = {lower_variant_fn}(meta); return function {lower_flat_result_fn}Inner(ctx) {{ - {debug_log_fn}('[{lower_flat_result_fn}()] args', {{ lowerMetas }}); + {debug_log_fn}('[{lower_flat_result_fn}()] args', {{ ctx }}); const v = ctx.vals[0]; const isNotResultObject = typeof v !== 'object' @@ -954,7 +960,7 @@ impl LowerIntrinsic { ctx.vals[0] = {{ tag: 'ok', val: v }}; }} - {lower_variant_fn}(lowerMetas)(ctx); + f(ctx); }}; }} "# diff --git a/crates/js-component-bindgen/src/intrinsics/mod.rs b/crates/js-component-bindgen/src/intrinsics/mod.rs index dc9e02379..5e4b5315c 100644 --- a/crates/js-component-bindgen/src/intrinsics/mod.rs +++ b/crates/js-component-bindgen/src/intrinsics/mod.rs @@ -1374,6 +1374,7 @@ pub fn render_intrinsics(args: RenderIntrinsicsArgs) -> Source { &Intrinsic::Lift(LiftIntrinsic::LiftFlatU8), &Intrinsic::Lift(LiftIntrinsic::LiftFlatU16), &Intrinsic::Lift(LiftIntrinsic::LiftFlatU32), + &Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64), ]); } diff --git a/crates/js-component-bindgen/src/transpile_bindgen.rs b/crates/js-component-bindgen/src/transpile_bindgen.rs index f31a5d6e7..3a61f55b4 100644 --- a/crates/js-component-bindgen/src/transpile_bindgen.rs +++ b/crates/js-component-bindgen/src/transpile_bindgen.rs @@ -5066,27 +5066,43 @@ pub fn gen_flat_lift_fn_js_expr( let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant).name(); let variant_ty = &component_types[*ty_idx]; let variant_flat_count = flat_count_js_expr(&variant_ty.abi.flat_count); - let mut cases_and_lifts_expr = String::from("["); + let variant_size32 = variant_ty.abi.size32; + let variant_align32 = variant_ty.abi.align32; + let variant_payload_offset32 = variant_ty.info.payload_offset32; + + let mut lift_metas_expr = String::from("["); for (name, maybe_ty) in &variant_ty.cases { - let (lift_fn_js, case_flat_count) = match maybe_ty { - Some(ty) => ( - gen_flat_lift_fn_js_expr(instantiator, ty, extra_resource_map), - flat_count_js_expr(&component_types.canonical_abi(ty).flat_count), - ), - None => ("null".into(), "0".into()), + let (lift_fn_js, case_size32, case_align32, case_flat_count) = match maybe_ty { + Some(ty) => { + let cabi_info = component_types.canonical_abi(ty); + ( + gen_flat_lift_fn_js_expr(instantiator, ty, extra_resource_map), + cabi_info.size32.to_string(), + cabi_info.align32.to_string(), + cabi_info + .flat_count(MAX_FLAT_PARAMS) + .map(|v| v.to_string()) + .unwrap_or_else(|| "null".into()), + ) + } + None => ("null".into(), "0".into(), "0".into(), "0".into()), }; - cases_and_lifts_expr.push_str(&format!( - "['{name}', {}, {}, {}, {}, {}, {}],", - lift_fn_js, - variant_ty.abi.size32, - variant_ty.abi.align32, - variant_ty.info.payload_offset32, - case_flat_count, - variant_flat_count, + + lift_metas_expr.push_str(&format!( + "['{name}', {lift_fn_js}, {case_size32}, {case_align32}, {case_flat_count}],", )); } - cases_and_lifts_expr.push(']'); - format!("{lift_fn}({cases_and_lifts_expr})") + lift_metas_expr.push(']'); + + format!( + "{lift_fn}({{ + caseMetas: {lift_metas_expr}, + variantSize32: {variant_size32}, + variantAlign32: {variant_align32}, + variantPayloadOffset32: {variant_payload_offset32}, + variantFlatCount: {variant_flat_count}, + }} )" + ) } InterfaceType::List(ty_idx) => { @@ -5189,40 +5205,61 @@ pub fn gen_flat_lift_fn_js_expr( instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum)); let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum).name(); let enum_ty = &component_types[*ty_idx]; - let size_32 = enum_ty.abi.size32; - let align_32 = enum_ty.abi.align32; - let payload_offset_32 = enum_ty.info.payload_offset32; - let flat_count = flat_count_js_expr(&enum_ty.abi.flat_count); + let enum_size32 = enum_ty.abi.size32; + let enum_align32 = enum_ty.abi.align32; + let enum_payload_offset32 = enum_ty.info.payload_offset32; + let enum_flat_count = flat_count_js_expr(&enum_ty.abi.flat_count); let mut elem_lifts_expr = String::from("["); for name in &enum_ty.names { elem_lifts_expr.push_str(&format!( - "['{name}', null, {size_32}, {align_32}, {payload_offset_32}, 0, {flat_count}]," + "['{name}', null, {enum_size32}, {enum_align32}, {enum_payload_offset32}]," )); } elem_lifts_expr.push(']'); - format!("{f}({elem_lifts_expr})") + format!( + r#" + {f}({{ + caseMetas: {elem_lifts_expr}, + variantSize32: {enum_size32}, + variantAlign32: {enum_align32}, + variantPayloadOffset32: {enum_payload_offset32}, + variantFlatCount: {enum_flat_count}, + }}) + "# + ) } InterfaceType::Option(ty_idx) => { instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOption)); let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOption).name(); let option_ty = &component_types[*ty_idx]; - let payload_offset_32 = option_ty.info.payload_offset32; - let align_32 = option_ty.abi.align32; - let size_32 = option_ty.abi.size32; - let flat_count = flat_count_js_expr(&option_ty.abi.flat_count); - let some_flat_count = - flat_count_js_expr(&component_types.canonical_abi(&option_ty.ty).flat_count); - let lift_fn_js = + let option_payload_offset32 = option_ty.info.payload_offset32; + let option_align32 = option_ty.abi.align32; + let option_size32 = option_ty.abi.size32; + let option_flat_count = flat_count_js_expr(&option_ty.abi.flat_count); + + let some_ty_abi = component_types.canonical_abi(&option_ty.ty); + let some_ty_flat_count = flat_count_js_expr(&some_ty_abi.flat_count); + let some_ty_size32 = some_ty_abi.size32; + let some_ty_align32 = some_ty_abi.align32; + let some_ty_lift_fn_js = gen_flat_lift_fn_js_expr(instantiator, &option_ty.ty, extra_resource_map); - // NOTE: options are treated as variants + format!( - "{f}([ - ['none', null, {size_32}, {align_32}, {payload_offset_32}, 0, {flat_count} ], - ['some', {lift_fn_js}, {size_32}, {align_32}, {payload_offset_32}, {some_flat_count}, {flat_count} ], - ])" + r#" + {f}({{ + caseMetas: [ + ['none', null, 0, 0, 0 ], + ['some', {some_ty_lift_fn_js}, {some_ty_size32}, {some_ty_align32}, {some_ty_flat_count} ], + ], + variantSize32: {option_size32}, + variantAlign32: {option_align32}, + variantPayloadOffset32: {option_payload_offset32}, + variantFlatCount: {option_flat_count}, + }}) + "# ) } @@ -5230,55 +5267,52 @@ pub fn gen_flat_lift_fn_js_expr( instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatResult)); let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatResult).name(); let result_ty = &component_types[*ty_idx]; + let result_size32 = result_ty.abi.size32; + let result_align32 = result_ty.abi.align32; + let result_payload_offset32 = result_ty.info.payload_offset32; let result_flat_count = flat_count_js_expr(&result_ty.abi.flat_count); - let mut cases_and_lifts_expr = String::from("["); + let mut cases_and_lifts_expr = String::from("["); if let Some(ok_ty) = result_ty.ok { - let ok_flat_count = - flat_count_js_expr(&component_types.canonical_abi(&ok_ty).flat_count); + let ok_ty_abi = component_types.canonical_abi(&ok_ty); + let ok_ty_size32 = ok_ty_abi.size32; + let ok_ty_align32 = ok_ty_abi.align32; + let ok_flat_count = flat_count_js_expr(&ok_ty_abi.flat_count); + let ok_ty_lift_fn = + gen_flat_lift_fn_js_expr(instantiator, &ok_ty, extra_resource_map); cases_and_lifts_expr.push_str(&format!( - "['ok', {}, {}, {}, {}, {}, {}],", - gen_flat_lift_fn_js_expr(instantiator, &ok_ty, extra_resource_map), - result_ty.abi.size32, - result_ty.abi.align32, - result_ty.info.payload_offset32, - ok_flat_count, - result_flat_count, + "['ok', {ok_ty_lift_fn}, {ok_ty_size32}, {ok_ty_align32}, {ok_flat_count}],", )) } else { - cases_and_lifts_expr.push_str(&format!( - "['ok', null, {}, {}, {}, 0, {}],", - result_ty.abi.size32, - result_ty.abi.align32, - result_ty.info.payload_offset32, - result_flat_count, - )); + cases_and_lifts_expr.push_str("['ok', null, 0, 0, 0],"); } if let Some(err_ty) = &result_ty.err { - let err_flat_count = - flat_count_js_expr(&component_types.canonical_abi(err_ty).flat_count); + let err_ty_abi = component_types.canonical_abi(err_ty); + let err_ty_size32 = err_ty_abi.size32; + let err_ty_align32 = err_ty_abi.align32; + let err_ty_flat_count = flat_count_js_expr(&err_ty_abi.flat_count); + let err_ty_lift_fn = + gen_flat_lift_fn_js_expr(instantiator, err_ty, extra_resource_map); cases_and_lifts_expr.push_str(&format!( - "['err', {}, {}, {}, {}, {}, {}],", - gen_flat_lift_fn_js_expr(instantiator, err_ty, extra_resource_map), - result_ty.abi.size32, - result_ty.abi.align32, - result_ty.info.payload_offset32, - err_flat_count, - result_flat_count, + "['err', {err_ty_lift_fn}, {err_ty_size32}, {err_ty_align32}, {err_ty_flat_count}],", )) } else { - cases_and_lifts_expr.push_str(&format!( - "['err', null, {}, {}, {}, 0, {}],", - result_ty.abi.size32, - result_ty.abi.align32, - result_ty.info.payload_offset32, - result_flat_count, - )); + cases_and_lifts_expr.push_str("['err', null, 0, 0, 0],"); } - cases_and_lifts_expr.push(']'); - format!("{lift_fn}({cases_and_lifts_expr})") + + format!( + r#" + {lift_fn}({{ + caseMetas: {cases_and_lifts_expr}, + variantSize32: {result_size32}, + variantAlign32: {result_align32}, + variantPayloadOffset32: {result_payload_offset32}, + variantFlatCount: {result_flat_count}, + }}) + "# + ) } InterfaceType::Own(ty_idx) => { @@ -5632,14 +5666,30 @@ pub fn gen_flat_lower_fn_js_expr( instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant)); let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant).name(); let variant_ty = &component_types[*ty_idx]; + let variant_flat_count = flat_count_js_expr(&variant_ty.abi.flat_count); let size32 = variant_ty.abi.size32; let align32 = variant_ty.abi.align32; let payload_offset32 = variant_ty.info.payload_offset32; let mut lower_metas_expr = String::from("["); for (name, maybe_ty) in variant_ty.cases.iter() { + let (case_size32, case_align32, case_flat_count) = if let Some(iface_ty) = maybe_ty + { + let cabi_info = component_types.canonical_abi(iface_ty); + ( + cabi_info.size32.to_string(), + cabi_info.align32.to_string(), + cabi_info + .flat_count(MAX_FLAT_PARAMS) + .map(|v| v.to_string()) + .unwrap_or_else(|| "null".into()), + ) + } else { + ("0".into(), "0".into(), "0".into()) + }; + lower_metas_expr.push_str(&format!( - "[ '{name}', {}, {size32}, {align32}, {payload_offset32} ],", + "[ '{name}', {}, {case_size32}, {case_align32}, {case_flat_count} ],", maybe_ty .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, &None)) .unwrap_or_else(|| "null".into()), @@ -5647,7 +5697,15 @@ pub fn gen_flat_lower_fn_js_expr( } lower_metas_expr.push(']'); - format!("{lower_fn}({lower_metas_expr})") + format!( + "{lower_fn}({{ + caseMetas: {lower_metas_expr}, + variantSize32: {size32}, + variantAlign32: {align32}, + variantPayloadOffset32: {payload_offset32}, + variantFlatCount: {variant_flat_count}, + }} )" + ) } InterfaceType::List(ty_idx) => { @@ -5750,36 +5808,60 @@ pub fn gen_flat_lower_fn_js_expr( instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum)); let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum).name(); let enum_ty = &component_types[*ty_idx]; - let size32 = enum_ty.abi.size32; - let align32 = enum_ty.abi.align32; - let payload_offset32 = enum_ty.info.payload_offset32; + let enum_size32 = enum_ty.abi.size32; + let enum_align32 = enum_ty.abi.align32; + let enum_flat_count = flat_count_js_expr(&enum_ty.abi.flat_count); + let enum_payload_offset32 = enum_ty.info.payload_offset32; let mut elem_lowers_expr = String::from("["); for name in &enum_ty.names { elem_lowers_expr.push_str(&format!( - "['{name}', null, {size32}, {align32}, {payload_offset32}]," + "['{name}', null, {enum_size32}, {enum_align32}, {enum_payload_offset32}]," )); } elem_lowers_expr.push(']'); - format!("{f}({elem_lowers_expr})") + format!( + r#" + {f}({{ + caseMetas: {elem_lowers_expr}, + variantSize32: {enum_size32}, + variantAlign32: {enum_align32}, + variantPayloadOffset32: {enum_payload_offset32}, + variantFlatCount: {enum_flat_count}, + }}) + "# + ) } InterfaceType::Option(ty_idx) => { instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOption)); let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOption).name(); let option_ty = &component_types[*ty_idx]; - let size32 = option_ty.abi.size32; - let align32 = option_ty.abi.align32; - let payload_offset32 = option_ty.info.payload_offset32; - let lower_fn_js = + let option_size32 = option_ty.abi.size32; + let option_align32 = option_ty.abi.align32; + let option_payload_offset32 = option_ty.info.payload_offset32; + let option_flat_count = flat_count_js_expr(&option_ty.abi.flat_count); + + let some_ty_abi = component_types.canonical_abi(&option_ty.ty); + let some_ty_flat_count = flat_count_js_expr(&some_ty_abi.flat_count); + let some_ty_size32 = some_ty_abi.size32; + let some_ty_align32 = some_ty_abi.align32; + let some_ty_lower_fn_js = gen_flat_lower_fn_js_expr(instantiator, &option_ty.ty, extra_resource_map); format!( - r#"{f}([ - [ 'none', null, {size32}, {align32}, {payload_offset32} ], - [ 'some', {lower_fn_js}, {size32}, {align32}, {payload_offset32} ], - ]) + r#" + {f}({{ + caseMetas: [ + [ 'none', null, 0, 0, 0 ], + [ 'some', {some_ty_lower_fn_js}, {some_ty_size32}, {some_ty_align32}, {some_ty_flat_count}], + ], + variantSize32: {option_size32}, + variantAlign32: {option_align32}, + variantPayloadOffset32: {option_payload_offset32}, + variantFlatCount: {option_flat_count}, + }}) "# ) } @@ -5788,9 +5870,11 @@ pub fn gen_flat_lower_fn_js_expr( instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatResult)); let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatResult).name(); let result_ty = &component_types[*ty_idx]; - let size32 = result_ty.abi.size32; - let align32 = result_ty.abi.align32; - let payload_offset32 = result_ty.info.payload_offset32; + let result_size32 = result_ty.abi.size32; + let result_align32 = result_ty.abi.align32; + let result_payload_offset32 = result_ty.info.payload_offset32; + let result_flat_count = flat_count_js_expr(&result_ty.abi.flat_count); + let ok_lower_fn_js = result_ty .ok .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, extra_resource_map)) @@ -5801,10 +5885,17 @@ pub fn gen_flat_lower_fn_js_expr( .unwrap_or_else(|| "null".into()); format!( - r#"{lower_fn}([ - [ 'ok', {ok_lower_fn_js}, {size32}, {align32}, {payload_offset32} ], - [ 'err', {err_lower_fn_js}, {size32}, {align32}, {payload_offset32} ], - ]) + r#" + {lower_fn}({{ + caseMetas: [ + [ 'ok', {ok_lower_fn_js}, {result_size32}, {result_align32}, {result_payload_offset32} ], + [ 'err', {err_lower_fn_js}, {result_size32}, {result_align32}, {result_payload_offset32} ], + ], + variantSize32: {result_size32}, + variantAlign32: {result_align32}, + variantPayloadOffset32: {result_payload_offset32}, + variantFlatCount: {result_flat_count}, + }}) "# ) } diff --git a/crates/test-components/wit/all.wit b/crates/test-components/wit/all.wit index d5e34386e..55f213d74 100644 --- a/crates/test-components/wit/all.wit +++ b/crates/test-components/wit/all.wit @@ -47,6 +47,7 @@ interface example-types { num(u32), str(string), float(f32), + float64(f64), maybe-u32(option), } diff --git a/packages/jco/test/common.js b/packages/jco/test/common.js index 3e44d948f..8376e28fa 100644 --- a/packages/jco/test/common.js +++ b/packages/jco/test/common.js @@ -58,13 +58,13 @@ export async function getDefaultComponentFixtures() { /** Check the values of a given stream (normally returned from a component) */ export async function checkStreamValues(args) { - const { stream, vals, typeName, assertEqFn, partial } = args ?? {}; - const expectedValues = args.expectedValues ?? []; + const { expectedValues, stream, typeName, assertEqFn, partial } = args ?? {}; + assert.isNotEmpty(expectedValues); // Ensure the values produced match expected const eq = assertEqFn ?? assert.equal; let iteratorRes; - for (const [idx, v] of vals.entries()) { + for (const [idx, v] of expectedValues.entries()) { const expected = expectedValues[idx] ?? v; iteratorRes = await stream.next(); assert.isFalse(iteratorRes.done); diff --git a/packages/jco/test/p3/async.js b/packages/jco/test/p3/async.js index 3bcd7715a..6cb51ded8 100644 --- a/packages/jco/test/p3/async.js +++ b/packages/jco/test/p3/async.js @@ -9,7 +9,7 @@ import { AsyncFunction, LOCAL_TEST_COMPONENTS_DIR } from "../common.js"; suite("Async (WASI P3)", () => { // see: https://github.com/bytecodealliance/jco/issues/1076 - test("incorrect task return params offloading", async () => { + test.concurrent("incorrect task return params offloading", async () => { const name = "async-flat-param-adder"; const { instance, cleanup } = await setupAsyncTest({ component: { @@ -36,7 +36,7 @@ suite("Async (WASI P3)", () => { await cleanup(); }); - test("lowered imports use trailing result pointer", async () => { + test.concurrent("lowered imports use trailing result pointer", async () => { const { instance, cleanup } = await setupAsyncTest({ asyncMode: "jspi", component: { @@ -69,12 +69,17 @@ suite("Async (WASI P3)", () => { }); // https://bytecodealliance.zulipchat.com/#narrow/channel/206238-general/topic/Should.20StringLift.20be.20emitted.20for.20async.20return.20values.3F/with/561133720 - test("simple async returns", async () => { + test.concurrent("simple async returns", async () => { const { instance, cleanup } = await setupAsyncTest({ component: { path: join(LOCAL_TEST_COMPONENTS_DIR, "async-simple-return.wasm"), imports: new WASIShim().getImportObject(), }, + // jco: { + // extraArgs: { + // minify: false + // } + // } }); assert.instanceOf(instance.getString, AsyncFunction); @@ -90,7 +95,7 @@ suite("Async (WASI P3)", () => { }); // https://github.com/bytecodealliance/jco/issues/1150 - test("simple bare async host imports", async () => { + test.concurrent("simple bare async host imports", async () => { const hostStr = "loaded-from-host"; const hostU32 = 43; @@ -124,7 +129,7 @@ suite("Async (WASI P3)", () => { await cleanup(); }); - test("async return of imported owned resource", async () => { + test.concurrent("async return of imported owned resource", async () => { class ExampleResource { constructor(id) { this.id = id; @@ -162,9 +167,7 @@ suite("Async (WASI P3)", () => { await cleanup(); }); - // TODO(tandr): this test currently fails and needs fixes in wit-bindgen-core: - // https://github.com/bytecodealliance/wit-bindgen/pull/1614 - test.skip("async return of bare imported owned resource", async () => { + test.concurrent("async return of bare imported owned resource", async () => { class ExampleResource { constructor(id) { this.id = id; @@ -203,7 +206,7 @@ suite("Async (WASI P3)", () => { }); // https://github.com/bytecodealliance/jco/issues/1601 - test("import of future-accepting host fn", async () => { + test.concurrent("import of future-accepting host fn", async () => { const { instance, cleanup } = await setupAsyncTest({ component: { path: join(LOCAL_TEST_COMPONENTS_DIR, "pass-back-host-resolved-future.wasm"), diff --git a/packages/jco/test/p3/future-lower.js b/packages/jco/test/p3/future-lowers.js similarity index 99% rename from packages/jco/test/p3/future-lower.js rename to packages/jco/test/p3/future-lowers.js index 6c81e17a6..9e789d317 100644 --- a/packages/jco/test/p3/future-lower.js +++ b/packages/jco/test/p3/future-lowers.js @@ -279,8 +279,7 @@ suite("future lowers", () => { } }); - // TODO(FIX): https://github.com/bytecodealliance/jco/issues/1428 - test.skip("variant", async () => { + test.concurrent("variant", async () => { assert.instanceOf(instance["jco:test-components/future-lower-async"].readFutureValueVariant, AsyncFunction); let vals = [ @@ -304,7 +303,10 @@ suite("future lowers", () => { ); } - vals = [{ tag: "float", val: 123.1 }]; + vals = [ + { tag: "float", val: 123.1 }, + { tag: "float64", val: 246.2 }, + ]; for (const [idx, v] of vals.entries()) { const returned = await instance["jco:test-components/future-lower-async"].readFutureValueVariant( Promise.resolve(v), @@ -313,7 +315,7 @@ suite("future lowers", () => { } }); - test("tuple", async () => { + test.concurrent("tuple", async () => { assert.instanceOf(instance["jco:test-components/future-lower-async"].readFutureValueTuple, AsyncFunction); let vals = [ diff --git a/packages/jco/test/p3/stream-lifts.js b/packages/jco/test/p3/stream-lifts.js index 6c4947fb2..c490dd8e6 100644 --- a/packages/jco/test/p3/stream-lifts.js +++ b/packages/jco/test/p3/stream-lifts.js @@ -75,7 +75,7 @@ suite("stream lifts", () => { assert.instanceOf(instance["jco:test-components/get-stream-async"].getStreamBool, AsyncFunction); const vals = [true, false]; const stream = await instance["jco:test-components/get-stream-async"].getStreamBool(vals); - await checkStreamValues({ stream, vals, typeName: "bool" }); + await checkStreamValues({ stream, expectedValues: vals, typeName: "bool" }); }); test.concurrent("u8/s8", async () => { @@ -87,7 +87,6 @@ suite("stream lifts", () => { let stream = await instance["jco:test-components/get-stream-async"].getStreamU8(vals); await checkStreamValues({ stream, - vals, expectedValues: toTypedArrayChunks(Uint8Array, vals), typeName: "u8", assertEqFn: assert.deepEqual, @@ -104,7 +103,6 @@ suite("stream lifts", () => { stream = await instance["jco:test-components/get-stream-async"].getStreamS8(vals); await checkStreamValues({ stream, - vals, expectedValues: toTypedArrayChunks(Int8Array, vals), typeName: "s8", assertEqFn: assert.deepEqual, @@ -137,7 +135,6 @@ suite("stream lifts", () => { let stream = await instance["jco:test-components/get-stream-async"].getStreamU16(vals); await checkStreamValues({ stream, - vals, expectedValues: toTypedArrayChunks(Uint16Array, vals), typeName: "u16", assertEqFn: assert.deepEqual, @@ -147,7 +144,6 @@ suite("stream lifts", () => { stream = await instance["jco:test-components/get-stream-async"].getStreamS16(vals); await checkStreamValues({ stream, - vals, expectedValues: toTypedArrayChunks(Int16Array, vals), typeName: "s16", assertEqFn: assert.deepEqual, @@ -163,7 +159,6 @@ suite("stream lifts", () => { let stream = await instance["jco:test-components/get-stream-async"].getStreamU32(vals); await checkStreamValues({ stream, - vals, expectedValues: toTypedArrayChunks(Uint32Array, vals), typeName: "u32", assertEqFn: assert.deepEqual, @@ -173,7 +168,6 @@ suite("stream lifts", () => { stream = await instance["jco:test-components/get-stream-async"].getStreamS32(vals); await checkStreamValues({ stream, - vals, expectedValues: toTypedArrayChunks(Int32Array, vals), typeName: "s32", assertEqFn: assert.deepEqual, @@ -189,7 +183,6 @@ suite("stream lifts", () => { let stream = await instance["jco:test-components/get-stream-async"].getStreamU64(vals); await checkStreamValues({ stream, - vals, expectedValues: toTypedArrayChunks(BigUint64Array, vals), typeName: "u64", assertEqFn: assert.deepEqual, @@ -206,7 +199,6 @@ suite("stream lifts", () => { stream = await instance["jco:test-components/get-stream-async"].getStreamS64(vals); await checkStreamValues({ stream, - vals, expectedValues: toTypedArrayChunks(BigInt64Array, vals), typeName: "s64", assertEqFn: assert.deepEqual, @@ -229,7 +221,6 @@ suite("stream lifts", () => { let stream = await instance["jco:test-components/get-stream-async"].getStreamF32(vals); await checkStreamValues({ stream, - vals, expectedValues: toTypedArrayChunks(Float32Array, vals), typeName: "f32", assertEqFn: assert.deepEqual, @@ -239,7 +230,6 @@ suite("stream lifts", () => { stream = await instance["jco:test-components/get-stream-async"].getStreamF64(vals); await checkStreamValues({ stream, - vals, expectedValues: toTypedArrayChunks(Float64Array, vals), typeName: "f64", assertEqFn: assert.deepEqual, @@ -252,7 +242,7 @@ suite("stream lifts", () => { let vals = ["hello", "world", "!"]; let stream = await instance["jco:test-components/get-stream-async"].getStreamString(vals); - await checkStreamValues({ stream, vals, typeName: "string" }); + await checkStreamValues({ stream, expectedValues: vals, typeName: "string" }); }); test.concurrent("record", async () => { @@ -265,13 +255,16 @@ suite("stream lifts", () => { { id: 3, idStr: "three" }, ]; let stream = await instance["jco:test-components/get-stream-async"].getStreamRecord(vals); - await checkStreamValues({ stream, vals, typeName: "record", assertEqFn: assert.deepEqual }); + await checkStreamValues({ stream, expectedValues: vals, typeName: "record", assertEqFn: assert.deepEqual }); }); test.concurrent("variant", async () => { const instance = await getInstance(); assert.instanceOf(instance["jco:test-components/get-stream-async"].getStreamVariant, AsyncFunction); + // NOTE we *could* get less than the expected values?!?! need to test against + // length of values we got back, because stream might return more/less? + const vals = [ { tag: "maybe-u32", val: 123 }, { tag: "maybe-u32", val: null }, @@ -284,7 +277,7 @@ suite("stream lifts", () => { // Ensure first two values match await checkStreamValues({ stream, - vals: [ + expectedValues: [ // TODO: wit type representation smoothing mismatch, // non-nullable option values are *not* wrapped as objects { tag: "maybe-u32", val: { tag: "some", val: 123 } }, @@ -298,7 +291,7 @@ suite("stream lifts", () => { // Check float member await checkStreamValues({ stream, - vals: vals.slice(2, 3), + expectedValues: vals.slice(2, 3), typeName: "variant", partial: true, assertEqFn: (value, expected) => { @@ -312,7 +305,7 @@ suite("stream lifts", () => { // Check rest of values await checkStreamValues({ stream, - vals: vals.slice(3), + expectedValues: vals.slice(3), typeName: "variant", assertEqFn: assert.deepEqual, }); @@ -327,7 +320,7 @@ suite("stream lifts", () => { let stream = await instance["jco:test-components/get-stream-async"].getStreamLayoutVariant(variants); await checkStreamValues({ stream, - vals: variants, + expectedValues: variants, typeName: "layout-variant", assertEqFn: assert.deepEqual, }); @@ -339,7 +332,7 @@ suite("stream lifts", () => { stream = await instance["jco:test-components/get-stream-async"].getStreamVariantStringRecord(records); await checkStreamValues({ stream, - vals: records, + expectedValues: records, typeName: "variant-string-record", assertEqFn: assert.deepEqual, }); @@ -355,7 +348,7 @@ suite("stream lifts", () => { [3, 4, "third"], ]; let stream = await instance["jco:test-components/get-stream-async"].getStreamTuple(vals); - await checkStreamValues({ stream, vals, typeName: "tuple", assertEqFn: assert.deepEqual }); + await checkStreamValues({ stream, expectedValues: vals, typeName: "tuple", assertEqFn: assert.deepEqual }); }); test.concurrent("tight tuple layout", async () => { @@ -370,7 +363,7 @@ suite("stream lifts", () => { const stream = await instance["jco:test-components/get-stream-async"].getStreamTightTuple(vals); await checkStreamValues({ stream, - vals, + expectedValues: vals, typeName: "tight-tuple", assertEqFn: assert.deepEqual, }); @@ -386,7 +379,7 @@ suite("stream lifts", () => { { first: false, second: false, third: true }, ]; let stream = await instance["jco:test-components/get-stream-async"].getStreamFlags(vals); - await checkStreamValues({ stream, vals, typeName: "flags", assertEqFn: assert.deepEqual }); + await checkStreamValues({ stream, expectedValues: vals, typeName: "flags", assertEqFn: assert.deepEqual }); }); test.concurrent("enum", async () => { @@ -395,7 +388,7 @@ suite("stream lifts", () => { let vals = ["first", "second", "third"]; let stream = await instance["jco:test-components/get-stream-async"].getStreamEnum(vals); - await checkStreamValues({ stream, vals, typeName: "enum", assertEqFn: assert.deepEqual }); + await checkStreamValues({ stream, expectedValues: vals, typeName: "enum", assertEqFn: assert.deepEqual }); }); test.concurrent("option", async () => { @@ -406,7 +399,6 @@ suite("stream lifts", () => { let stream = await instance["jco:test-components/get-stream-async"].getStreamOptionString(vals); await checkStreamValues({ stream, - vals, typeName: "option", assertEqFn: assert.deepEqual, expectedValues: [ @@ -425,7 +417,6 @@ suite("stream lifts", () => { await checkStreamValues({ stream, - vals, typeName: "list", assertEqFn: assert.deepEqual, expectedValues: toTypedArrays(Uint8Array, vals), @@ -437,7 +428,12 @@ suite("stream lifts", () => { assert.instanceOf(instance["jco:test-components/get-stream-async"].getStreamListString, AsyncFunction); let vals = [["first", "second", "third"], []]; let stream = await instance["jco:test-components/get-stream-async"].getStreamListString(vals); - await checkStreamValues({ stream, vals, typeName: "list", assertEqFn: assert.deepEqual }); + await checkStreamValues({ + stream, + expectedValues: vals, + typeName: "list", + assertEqFn: assert.deepEqual, + }); }); test.concurrent("list", async () => { @@ -448,7 +444,12 @@ suite("stream lifts", () => { [0, 0, 0, 0, 0], ]; let stream = await instance["jco:test-components/get-stream-async"].getStreamFixedListU32(vals); - await checkStreamValues({ stream, vals, typeName: "list", assertEqFn: assert.deepEqual }); + await checkStreamValues({ + stream, + expectedValues: vals, + typeName: "list", + assertEqFn: assert.deepEqual, + }); // TODO: test misuse of fixed length list }); @@ -469,7 +470,12 @@ suite("stream lifts", () => { [], ]; let stream = await instance["jco:test-components/get-stream-async"].getStreamListRecord(vals); - await checkStreamValues({ stream, vals, typeName: "list", assertEqFn: assert.deepEqual }); + await checkStreamValues({ + stream, + expectedValues: vals, + typeName: "list", + assertEqFn: assert.deepEqual, + }); }); test.concurrent("list", async () => { @@ -489,7 +495,7 @@ suite("stream lifts", () => { const stream = await instance["jco:test-components/get-stream-async"].getStreamListPaddedRecord(vals); await checkStreamValues({ stream, - vals, + expectedValues: vals, typeName: "list", assertEqFn: assert.deepEqual, }); @@ -503,7 +509,12 @@ suite("stream lifts", () => { { tag: "err", val: "nope" }, ]; let stream = await instance["jco:test-components/get-stream-async"].getStreamResultString(vals); - await checkStreamValues({ stream, vals, typeName: "result", assertEqFn: assert.deepEqual }); + await checkStreamValues({ + stream, + expectedValues: vals, + typeName: "result", + assertEqFn: assert.deepEqual, + }); }); test.concurrent("example-resource", async () => { @@ -561,7 +572,6 @@ suite("stream lifts", () => { await checkStreamValues({ stream, - vals, typeName: "example-resource#get-id (output)", expectedValues: [2, 1, 0], });