use crate::executor::VirtualMachine;
use shape_runtime::context::ExecutionContext;
use shape_value::heap_value::{ChannelData, HeapKind, HeapValue};
use shape_value::{KindedSlot, NativeKind, VMError};
use std::sync::Arc;
#[inline]
fn type_error(msg: impl Into<String>) -> VMError {
VMError::RuntimeError(msg.into())
}
#[inline]
fn as_channel(slot: &KindedSlot) -> Result<Arc<ChannelData>, VMError> {
if !matches!(slot.kind, NativeKind::Ptr(HeapKind::Channel)) {
return Err(type_error(format!(
"Channel method receiver must be a Channel (got kind {:?})",
slot.kind
)));
}
let bits = slot.slot.raw();
if bits == 0 {
return Err(type_error("Channel method receiver slot bits null"));
}
let arc = unsafe { Arc::<ChannelData>::from_raw(bits as *const ChannelData) };
let cloned = Arc::clone(&arc);
let _ = Arc::into_raw(arc);
Ok(cloned)
}
pub fn v2_channel_send(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Channel.send() requires exactly 1 argument (value)",
));
}
let ch: Arc<ChannelData> = as_channel(&args[0])?;
let queued = args[1].clone();
if ch.send(queued).is_err() {
return Err(type_error("Channel.send() on a closed channel"));
}
let result = ch;
Ok(KindedSlot::from_channel(result))
}
pub fn v2_channel_recv(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("Channel.recv() takes no arguments"));
}
let ch: Arc<ChannelData> = as_channel(&args[0])?;
if let Some(slot) = ch.try_recv() {
return Ok(slot);
}
if ch.is_closed() {
return Err(type_error(
"Channel.recv() on a closed and empty channel",
));
}
Err(VMError::NotImplemented(
"Channel.recv() blocking-on-empty path requires the \
§2.7.4 task-scheduler boundary integration (cross-task \
await-style suspend/resume) — phase-2c follow-up. Use \
try_recv() for the non-blocking poll variant. \
ADR-006 §2.7.20 / §2.7.4."
.to_string(),
))
}
pub fn v2_channel_try_recv(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error(
"Channel.try_recv() takes no arguments",
));
}
let ch: Arc<ChannelData> = as_channel(&args[0])?;
Ok(ch.try_recv().unwrap_or_else(KindedSlot::none))
}
pub fn v2_channel_close(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("Channel.close() takes no arguments"));
}
let ch: Arc<ChannelData> = as_channel(&args[0])?;
ch.close();
Ok(KindedSlot::from_channel(ch))
}
pub fn v2_channel_is_closed(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error(
"Channel.is_closed() takes no arguments",
));
}
let ch: Arc<ChannelData> = as_channel(&args[0])?;
Ok(KindedSlot::from_bool(ch.is_closed()))
}
pub fn v2_channel_is_sender(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error(
"Channel.is_sender() takes no arguments",
));
}
let _ = as_channel(&args[0])?;
Err(VMError::NotImplemented(
"Channel.is_sender(): the W15 rebuild (ADR-006 §2.7.20 / \
Q21, 2026-05-10) collapses sender/receiver endpoints into \
a single Arc<ChannelData> carrier — the role-check has no \
semantic answer. Re-introducing typed sender/receiver \
endpoints is a phase-2c follow-up."
.to_string(),
))
}