use crate::intrinsics::Intrinsic;
use crate::intrinsics::component::ComponentIntrinsic;
use crate::intrinsics::p3::{async_stream::AsyncStreamIntrinsic, error_context::ErrCtxIntrinsic};
use crate::intrinsics::string::StringIntrinsic;
use crate::source::Source;
use super::conversion::ConversionIntrinsic;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum LowerIntrinsic {
LowerFlatBool,
LowerFlatS8,
LowerFlatU8,
LowerFlatS16,
LowerFlatU16,
LowerFlatS32,
LowerFlatU32,
LowerFlatS64,
LowerFlatU64,
LowerFlatFloat32,
LowerFlatFloat64,
LowerFlatChar,
LowerFlatStringUtf8,
LowerFlatStringUtf16,
LowerFlatRecord,
LowerFlatVariant,
LowerFlatList,
LowerFlatTuple,
LowerFlatFlags,
LowerFlatEnum,
LowerFlatOption,
LowerFlatResult,
LowerFlatOwn,
LowerFlatBorrow,
LowerFlatFuture,
LowerFlatStream,
LowerFlatErrorContext,
}
impl LowerIntrinsic {
pub fn deps() -> &'static [&'static Intrinsic] {
&[]
}
pub fn get_global_names() -> impl IntoIterator<Item = &'static str> {
[]
}
pub fn name(&self) -> &'static str {
match self {
Self::LowerFlatBool => "_lowerFlatBool",
Self::LowerFlatS8 => "_lowerFlatS8",
Self::LowerFlatU8 => "_lowerFlatU8",
Self::LowerFlatS16 => "_lowerFlatS16",
Self::LowerFlatU16 => "_lowerFlatU16",
Self::LowerFlatS32 => "_lowerFlatS32",
Self::LowerFlatU32 => "_lowerFlatU32",
Self::LowerFlatS64 => "_lowerFlatS64",
Self::LowerFlatU64 => "_lowerFlatU64",
Self::LowerFlatFloat32 => "_lowerFlatFloat32",
Self::LowerFlatFloat64 => "_lowerFlatFloat64",
Self::LowerFlatChar => "_lowerFlatChar",
Self::LowerFlatStringUtf8 => "_lowerFlatStringUTF8",
Self::LowerFlatStringUtf16 => "_lowerFlatStringUTF16",
Self::LowerFlatRecord => "_lowerFlatRecord",
Self::LowerFlatVariant => "_lowerFlatVariant",
Self::LowerFlatList => "_lowerFlatList",
Self::LowerFlatTuple => "_lowerFlatTuple",
Self::LowerFlatFlags => "_lowerFlatFlags",
Self::LowerFlatEnum => "_lowerFlatEnum",
Self::LowerFlatOption => "_lowerFlatOption",
Self::LowerFlatResult => "_lowerFlatResult",
Self::LowerFlatOwn => "_lowerFlatOwn",
Self::LowerFlatBorrow => "_lowerFlatBorrow",
Self::LowerFlatFuture => "_lowerFlatFuture",
Self::LowerFlatStream => "_lowerFlatStream",
Self::LowerFlatErrorContext => "_lowerFlatErrorContext",
}
}
pub fn render(&self, output: &mut Source) {
match self {
Self::LowerFlatBool => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatBool(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatBool()] args', {{ memory, vals, storagePtr, storageLen }});
if (vals.length !== 1) {{
throw new Error('unexpected number (' + vals.length + ') of core vals (expected 1)');
}}
if (vals[0] !== 0 && vals[0] !== 1) {{ throw new Error('invalid value for core value representing bool'); }}
new DataView(memory.buffer).setUint32(storagePtr, vals[0], true);
return 1;
}}
"));
}
Self::LowerFlatS8 => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatS8(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatS8()] args', {{ memory, vals, storagePtr, storageLen }});
if (vals.length !== 1) {{
throw new Error('unexpected number (' + vals.length + ') of core vals (expected 1)');
}}
if (vals[0] > 127 || vals[0] < -128) {{ throw new Error('invalid value for core value representing s8'); }}
new DataView(memory.buffer).setInt32(storagePtr, vals[0], true);
return 8;
}}
"));
}
Self::LowerFlatU8 => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!(r#"
function _lowerFlatU8(ctx) {{
{debug_log_fn}('[_lowerFlatU8()] args', ctx);
const {{ memory, realloc, vals, storagePtr, storageLen }} = ctx;
if (vals.length !== 1) {{
throw new Error('unexpected number (' + vals.length + ') of core vals (expected 1)');
}}
if (vals[0] > 255 || vals[0] < 0) {{ throw new Error('invalid value for core value representing u8'); }}
if (!memory) {{ throw new Error("missing memory for lower"); }}
new DataView(memory.buffer).setUint32(storagePtr, vals[0], true);
// TODO: ALIGNMENT IS WRONG?
return 1;
}}
"#));
}
Self::LowerFlatS16 => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatS16(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatS16()] args', {{ memory, vals, storagePtr, storageLen }});
if (vals.length !== 1) {{
throw new Error('unexpected number (' + vals.length + ') of core vals (expected 1)');
}}
if (vals[0] > 32_767 || vals[0] < -32_768) {{ throw new Error('invalid value for core value representing s16'); }}
new DataView(memory.buffer).setInt16(storagePtr, vals[0], true);
return 2;
}}
"));
}
Self::LowerFlatU16 => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatU16(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatU16()] args', {{ memory, vals, storagePtr, storageLen }});
if (vals.length !== 1) {{
throw new Error('unexpected number (' + vals.length + ') of core vals (expected 1)');
}}
if (vals[0] > 65_535 || vals[0] < 0) {{ throw new Error('invalid value for core value representing u16'); }}
new DataView(memory.buffer).setUint16(storagePtr, vals[0], true);
return 2;
}}
"));
}
Self::LowerFlatS32 => {
let debug_log_fn = Intrinsic::DebugLog.name();
let lower_flat_s32_fn = self.name();
output.push_str(&format!("
function {lower_flat_s32_fn}(ctx) {{
{debug_log_fn}('[{lower_flat_s32_fn}()] args', {{ ctx }});
const {{ memory, realloc, vals, storagePtr, storageLen }} = ctx;
if (vals.length !== 1) {{
throw new Error('unexpected number (' + vals.length + ') of core vals (expected 1)');
}}
if (vals[0] > 2_147_483_647 || vals[0] < -2_147_483_648) {{ throw new Error('invalid value for core value representing s32'); }}
// TODO(refactor): fail loudly on misaligned flat lowers?
const rem = ctx.storagePtr % 4;
if (rem !== 0) {{ ctx.storagePtr += (4 - rem); }}
new DataView(memory.buffer).setInt32(storagePtr, vals[0], true);
return 4;
}}
"));
}
Self::LowerFlatU32 => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!(r#"
function _lowerFlatU32(ctx) {{
{debug_log_fn}('[_lowerFlatU32()] args', {{ ctx }});
const {{ memory, realloc, vals, storagePtr, storageLen }} = ctx;
if (vals.length !== 1) {{ throw new Error('expected single value to lower, got (' + vals.length + ')'); }}
if (vals[0] > 4_294_967_295 || vals[0] < 0) {{ throw new Error('invalid value for core value representing u32'); }}
// TODO(refactor): fail loudly on misaligned flat lowers?
const rem = ctx.storagePtr % 4;
if (rem !== 0) {{ ctx.storagePtr += (4 - rem); }}
new DataView(memory.buffer).setUint32(storagePtr, vals[0], true);
return 4;
}}
"#));
}
Self::LowerFlatS64 => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatS64(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatS64()] args', {{ memory, vals, storagePtr, storageLen }});
if (vals.length !== 1) {{ throw new Error('unexpected number of core vals'); }}
if (vals[0] > 9_223_372_036_854_775_807n || vals[0] < -9_223_372_036_854_775_808n) {{ throw new Error('invalid value for core value representing s64'); }}
new DataView(memory.buffer).setBigInt64(storagePtr, vals[0], true);
return 8;
}}
"));
}
Self::LowerFlatU64 => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatU64(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatU64()] args', {{ memory, vals, storagePtr, storageLen }});
if (vals.length !== 1) {{ throw new Error('unexpected number of core vals'); }}
if (vals[0] > 18_446_744_073_709_551_615n || vals[0] < 0n) {{ throw new Error('invalid value for core value representing u64'); }}
new DataView(memory.buffer).setBigUint64(storagePtr, vals[0], true);
return 8;
}}
"));
}
Self::LowerFlatFloat32 => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatFloat32(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatFloat32()] args', {{ memory, vals, storagePtr, storageLen }});
if (vals.length !== 1) {{ throw new Error('unexpected number of core vals'); }}
new DataView(memory.buffer).setFloat32(storagePtr, vals[0], true);
return 4;
}}
"));
}
Self::LowerFlatFloat64 => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatFloat64(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatFloat64()] args', {{ memory, vals, storagePtr, storageLen }});
if (vals.length !== 1) {{ throw new Error('unexpected number of core vals'); }}
new DataView(memory.buffer).setFloat64(storagePtr, vals[0], true);
return 8;
}}
"));
}
Self::LowerFlatChar => {
let i32_to_char_fn = Intrinsic::Conversion(ConversionIntrinsic::I32ToChar).name();
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatChar(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatChar()] args', {{ memory, vals, storagePtr, storageLen }});
if (vals.length !== 1) {{ throw new Error('unexpected number of core vals'); }}
new DataView(memory.buffer).setUint32(storagePtr, {i32_to_char_fn}(vals[0]), true);
return 4;
}}
"));
}
Self::LowerFlatStringUtf16 => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatStringUTF16(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatStringUTF16()] args', {{ memory, vals, storagePtr, storageLen }});
const start = new DataView(memory.buffer).getUint32(storagePtr, vals[0], true);
const codeUnits = new DataView(memory.buffer).getUint32(storagePtr, vals[0] + 4, true);
var bytes = new Uint16Array(memory.buffer, start, codeUnits);
if (memory.buffer.byteLength < start + bytes.byteLength) {{
throw new Error('memory out of bounds');
}}
if (storageLen !== undefined && storageLen !== bytes.byteLength) {{
throw new Error('storage length (' + storageLen + ') != (' + bytes.byteLength + ')');
}}
new Uint16Array(memory.buffer, storagePtr).set(bytes);
return bytes.byteLength;
}}
"));
}
Self::LowerFlatStringUtf8 => {
let debug_log_fn = Intrinsic::DebugLog.name();
let utf8_encode_fn = Intrinsic::String(StringIntrinsic::Utf8Encode).name();
output.push_str(&format!("
function _lowerFlatStringUTF8(ctx) {{
{debug_log_fn}('[_lowerFlatStringUTF8()] args', ctx);
const {{ memory, realloc, vals, storagePtr, storageLen }} = ctx;
const s = vals[0];
const {{ ptr, len, codepoints }} = {utf8_encode_fn}(vals[0], realloc, memory);
const view = new DataView(memory.buffer);
view.setUint32(storagePtr, ptr, true);
view.setUint32(storagePtr + 4, codepoints, true);
return len;
}}
"));
}
Self::LowerFlatRecord => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatRecord(fieldMetas) {{
return (size, memory, vals, storagePtr, storageLen) => {{
const params = [...arguments].slice(5);
{debug_log_fn}('[_lowerFlatRecord()] args', {{
size,
memory,
vals,
storagePtr,
storageLen,
params,
fieldMetas
}});
const [start] = vals;
if (storageLen !== undefined && size !== undefined && size > storageLen) {{
throw new Error('not enough storage remaining for record flat lower');
}}
const data = new Uint8Array(memory.buffer, start, size);
new Uint8Array(memory.buffer, storagePtr, size).set(data);
return data.byteLength;
}}
}}
"));
}
Self::LowerFlatVariant => {
let debug_log_fn = Intrinsic::DebugLog.name();
let lower_flat_variant_fn = self.name();
let lower_u8_fn = Self::LowerFlatU8.name();
let lower_u16_fn = Self::LowerFlatU16.name();
let lower_u32_fn = Self::LowerFlatU32.name();
output.push_str(&format!(r#"
function {lower_flat_variant_fn}(lowerMetas) {{
return function {lower_flat_variant_fn}Inner(ctx) {{
{debug_log_fn}('[{lower_flat_variant_fn}()] args', ctx);
const {{ memory, realloc, vals, storageLen, componentIdx }} = ctx;
let storagePtr = ctx.storagePtr;
const {{ tag, val }} = vals[0];
const disc = lowerMetas.findIndex(m => m[0] === tag);
if (disc === -1) {{
throw new Error(`invalid variant tag/discriminant [${{tag}}] (valid tags: ${{variantMetas.map(m => m[0])}})`);
}}
const [ _tag, lowerFn, size32, align32, payloadOffset32 ] = lowerMetas[disc];
const originalPtr = ctx.resultPtr;
ctx.vals = [disc];
let discLowerRes;
if (lowerMetas.length < 256) {{
discLowerRes = {lower_u8_fn}(ctx);
}} else if (lowerMetas.length >= 256 && lowerMetas.length < 65536) {{
discLowerRes = {lower_u16_fn}(ctx);
}} else if (lowerMetas.length >= 65536 && lowerMetas.length < 4_294_967_296) {{
discLowerRes = {lower_u32_fn}(ctx);
}} else {{
throw new Error('unsupported number of cases [' + lowerMetas.legnth + ']');
}}
ctx.resultPtr = originalPtr + payloadOffset32;
let payloadBytesWritten = 0;
if (lowerFn) {{
lowerFn({{
memory,
realloc,
vals: [val],
storagePtr,
storageLen,
componentIdx,
}});
}}
let bytesWritten = payloadOffset + payloadBytesWritten;
const rem = ctx.storagePtr % align32;
if (rem !== 0) {{
const pad = align32 - rem;
ctx.storagePtr += pad;
bytesWritten += pad;
}}
return bytesWritten;
}}
}}
"#));
}
Self::LowerFlatList => {
let debug_log_fn = Intrinsic::DebugLog.name();
let lower_flat_list_fn = self.name();
output.push_str(&format!(r#"
function {lower_flat_list_fn}(args) {{
const {{ elemLowerFn }} = args;
if (!elemLowerFn) {{ throw new TypeError("missing/invalid element lower fn for list"); }}
return function {lower_flat_list_fn}Inner(ctx) {{
{debug_log_fn}('[_lowerFlatList()] args', {{ ctx }});
if (ctx.params.length < 2) {{ throw new Error('insufficient params left to lower list'); }}
const storagePtr = ctx.params[0];
const elemCount = ctx.params[1];
ctx.params = ctx.params.slice(2);
if (ctx.useDirectParams) {{
const list = ctx.vals[0];
if (!list) {{ throw new Error("missing direct param value"); }}
const elemLowerCtx = {{ storagePtr, memory: ctx.memory }};
for (let idx = 0; idx < list.length; idx++) {{
elemLowerCtx.vals = list.slice(idx, idx+1);
elemLowerCtx.storagePtr += elemLowerFn(elemLowerCtx);
}}
const bytesLowered = elemLowerCtx.storagePtr - ctx.storagePtr;
ctx.storagePtr = elemLowerCtx.storagePtr;
return bytesLowered;
}}
if (ctx.vals.length !== 2) {{
throw new Error('indirect parameter loading must have a pointer and length as vals');
}}
let [valStartPtr, valLen] = ctx.vals;
const totalSizeBytes = valLen * size;
if (ctx.storageLen !== undefined && totalSizeBytes > ctx.storageLen) {{
throw new Error('not enough storage remaining for list flat lower');
}}
const data = new Uint8Array(memory.buffer, valStartPtr, totalSizeBytes);
new Uint8Array(memory.buffer, storagePtr, totalSizeBytes).set(data);
return totalSizeBytes;
}}
}}
"#));
}
Self::LowerFlatTuple => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatTuple(size, memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatTuple()] args', {{ size, memory, vals, storagePtr, storageLen }});
let [start, len] = vals;
if (storageLen !== undefined && len > storageLen) {{
throw new Error('not enough storage remaining for tuple flat lower');
}}
const data = new Uint8Array(memory.buffer, start, len);
new Uint8Array(memory.buffer, storagePtr, len).set(data);
return data.byteLength;
}}
"));
}
Self::LowerFlatFlags => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatFlags(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatFlags()] args', {{ size, memory, vals, storagePtr, storageLen }});
if (vals.length !== 1) {{ throw new Error('unexpected number of core vals'); }}
new DataView(memory.buffer).setInt32(storagePtr, vals[0], true);
return 4;
}}
"));
}
Self::LowerFlatEnum => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatEnum(size, memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatEnum()] args', {{ size, memory, vals, storagePtr, storageLen }});
let [start] = vals;
if (storageLen !== undefined && size !== undefined && size > storageLen) {{
throw new Error('not enough storage remaining for enum flat lower');
}}
const data = new Uint8Array(memory.buffer, start, size);
new Uint8Array(memory.buffer, storagePtr, size).set(data);
return data.byteLength;
}}
"));
}
Self::LowerFlatOption => {
let debug_log_fn = Intrinsic::DebugLog.name();
let lower_flat_option_fn = self.name();
let lower_variant_fn = Self::LowerFlatVariant.name();
output.push_str(&format!(
"
function {lower_flat_option_fn}(lowerMetas) {{
function {lower_flat_option_fn}Inner(ctx) {{
{debug_log_fn}('[{lower_flat_option_fn}()] args', {{ ctx }});
return {lower_variant_fn}(lowerMetas)(ctx);
}}
}}
"
));
}
Self::LowerFlatResult => {
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) {{
return function {lower_flat_result_fn}Inner(ctx) {{
{debug_log_fn}('[{lower_flat_result_fn}()] args', {{ lowerMetas }});
return {lower_variant_fn}(lowerMetas)(ctx);
}};
}}
"#
));
}
Self::LowerFlatOwn => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatOwn(size, memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatOwn()] args', {{ size, memory, vals, storagePtr, storageLen }});
throw new Error('flat lower for owned resources not yet implemented!');
}}
"));
}
Self::LowerFlatBorrow => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatBorrow(size, memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatBorrow()] args', {{ size, memory, vals, storagePtr, storageLen }});
throw new Error('flat lower for borrowed resources not yet implemented!');
}}
"));
}
Self::LowerFlatFuture => {
let debug_log_fn = Intrinsic::DebugLog.name();
output.push_str(&format!("
function _lowerFlatFuture(memory, vals, storagePtr, storageLen) {{
{debug_log_fn}('[_lowerFlatFuture()] args', {{ size, memory, vals, storagePtr, storageLen }});
throw new Error('flat lower for futures not yet implemented!');
}}
"));
}
Self::LowerFlatStream => {
let debug_log_fn = Intrinsic::DebugLog.name();
let global_stream_map = AsyncStreamIntrinsic::GlobalStreamMap.name();
let external_stream_class = AsyncStreamIntrinsic::ExternalStreamClass.name();
let internal_stream_class = AsyncStreamIntrinsic::InternalStreamClass.name();
output.push_str(&format!(
r#"
function _lowerFlatStream(streamTableIdx, ctx) {{
{debug_log_fn}('[_lowerFlatStream()] args', {{ streamTableIdx, ctx }});
const {{
memory,
realloc,
vals,
storagePtr: resultPtr,
}} = ctx;
const externalStream = vals[0];
if (!externalStream || !(externalStream instanceof {external_stream_class})) {{
throw new Error("invalid external stream value");
}}
const globalRep = externalStream.globalRep();
const internalStream = {global_stream_map}.get(globalRep);
if (!internalStream || !(internalStream instanceof {internal_stream_class})) {{
throw new Error(`failed to find internal stream with rep [${{globalRep}}]`);
}}
const readEnd = internalStream.readEnd();
const waitableIdx = readEnd.waitableIdx();
// Write the idx of the waitable to memory (a waiting async task or caller)
if (resultPtr) {{
new DataView(memory.buffer).setUint32(resultPtr, waitableIdx, true);
}}
// TODO: if we flat lower another way (host -> guest async) we need to actually
// modify the guests table's afresh, we can't just use the global rep!
// (can detect this by whether the external stream has a rep or not)
return waitableIdx
}}
"#
));
}
Self::LowerFlatErrorContext => {
let debug_log_fn = Intrinsic::DebugLog.name();
let lower_u32_fn = Self::LowerFlatU32.name();
let create_local_handle_fn = ErrCtxIntrinsic::CreateLocalHandle.name();
let err_ctx_global_ref_count_add_fn = ErrCtxIntrinsic::GlobalRefCountAdd.name();
let get_or_create_async_state_fn = ComponentIntrinsic::GetOrCreateAsyncState.name();
let global_tbl = ErrCtxIntrinsic::ComponentGlobalTable.name();
let get_local_tbl_fn = ErrCtxIntrinsic::GetLocalTable.name();
output.push_str(&format!(r#"
function _lowerFlatErrorContext(errCtxTableIdx, ctx) {{
{debug_log_fn}('[_lowerFlatErrorContext()] args', {{ errCtxTableIdx, ctx }});
const {{ memory, realloc, vals, storagePtr, storageLen, componentIdx }} = ctx;
const errCtxGlobalRep = vals[0];
const globalTable = {global_tbl}.get();
const globalErrCtx = globalTable.get(errCtxGlobalRep);
// Clean up the previous error context, if necessary
const prevComponentState = {get_or_create_async_state_fn}(globalErrCtx.componentIdx);
const prevLocalErrCtx = prevComponentState.handles.get(globalErrCtx.waitableIdx);
if (prevLocalErrCtx.refCount === 0) {{
const removed = prevComponentState.remove(globalErrCtx.waitableIdx);
if (!removed) {{
throw new Error(`failed to remove err ctx [${{globalErrCtx.waitableIdx}}], component [${{globalErrCtx.componentIdx}}]`);
}}
const prevLocalErrCtxTable = {get_local_tbl_fn}(globalErrCtx.componentIdx, globalErrCtx.localTableIdx);
prevLocalErrCtxTable.remove(globalErrCtx.localIdx)
}}
// Insert the error context into the destination tables
const localErrCtxTable = {get_local_tbl_fn}(componentIdx, errCtxTableIdx, {{ upsert: true }});
let handle = localErrCtxTable.get(componentIdx, errCtxTableIdx, );
if (handle === undefined) {{
const {{ waitableIdx, localIdx }} = {create_local_handle_fn}(
componentIdx,
localErrCtxTable,
errCtxGlobalRep,
);
handle = waitableIdx;
}} else {{
const cstate = {get_or_create_async_state_fn}(componentIdx);
const localErrCtx = cstate.handles.get(handle);
localErrCtx.refCount += 1;
localErrCtx.componentIdx = componentIdx;
localErrCtx.localIdx = errCtx.localIdx;
localErrCtx.localTableIdx = errCtxTableIdx;
}}
{err_ctx_global_ref_count_add_fn}(errCtxGlobalRep, -1);
{lower_u32_fn}({{ memory, realloc, vals: [handle], storagePtr, storageLen, componentIdx }});
}}
"#));
}
}
}
}