use crate::executor::VirtualMachine;
use shape_runtime::context::ExecutionContext;
use shape_value::heap_value::HeapKind;
use shape_value::{KindedSlot, NativeKind, VMError};
use std::sync::Arc;
#[inline]
fn recv_instant<'a>(args: &'a [KindedSlot]) -> Result<&'a std::time::Instant, VMError> {
if args.is_empty() {
return Err(VMError::RuntimeError(
"Instant method invoked with no receiver".to_string(),
));
}
match args[0].kind {
NativeKind::Ptr(HeapKind::Instant) => {
let bits = args[0].slot.raw();
Ok(unsafe { &*(bits as *const std::time::Instant) })
}
other => Err(VMError::RuntimeError(format!(
"Instant method expected Instant receiver, got {:?}",
other
))),
}
}
pub fn v2_elapsed(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let instant = recv_instant(args)?;
let secs = instant.elapsed().as_secs_f64();
Ok(KindedSlot::from_number(secs))
}
pub fn v2_elapsed_ms(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let instant = recv_instant(args)?;
let ms = instant.elapsed().as_secs_f64() * 1000.0;
Ok(KindedSlot::from_number(ms))
}
pub fn v2_elapsed_us(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let instant = recv_instant(args)?;
let us = instant.elapsed().as_secs_f64() * 1_000_000.0;
Ok(KindedSlot::from_number(us))
}
pub fn v2_elapsed_ns(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let instant = recv_instant(args)?;
let ns = instant.elapsed().as_nanos() as i64;
Ok(KindedSlot::from_int(ns))
}
pub fn v2_duration_since(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let this = recv_instant(args)?;
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Instant.duration_since requires one Instant argument".to_string(),
));
}
let other = match args[1].kind {
NativeKind::Ptr(HeapKind::Instant) => {
let bits = args[1].slot.raw();
unsafe { &*(bits as *const std::time::Instant) }
}
other => {
return Err(VMError::RuntimeError(format!(
"Instant.duration_since expected Instant argument, got {:?}",
other
)));
}
};
let ms = this.duration_since(*other).as_secs_f64() * 1000.0;
Ok(KindedSlot::from_number(ms))
}
pub fn v2_to_string(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let instant = recv_instant(args)?;
let elapsed = instant.elapsed();
let s = format!("Instant(elapsed: {:.6}s)", elapsed.as_secs_f64());
Ok(KindedSlot::from_string_arc(Arc::new(s)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::executor::{VMConfig, VirtualMachine};
use shape_value::ValueSlot;
fn create_test_vm() -> VirtualMachine {
VirtualMachine::new(VMConfig::default())
}
fn make_instant_arg(instant: std::time::Instant) -> KindedSlot {
let arc = Arc::new(instant);
let bits = Arc::into_raw(arc) as u64;
KindedSlot::new(
ValueSlot::from_raw(bits),
NativeKind::Ptr(HeapKind::Instant),
)
}
#[test]
fn test_elapsed_returns_number() {
let mut vm = create_test_vm();
let args = [make_instant_arg(std::time::Instant::now())];
let result = v2_elapsed(&mut vm, &args, None).unwrap();
assert_eq!(result.kind, NativeKind::Float64);
let secs = result.as_f64().unwrap();
assert!(secs >= 0.0);
assert!(secs < 1.0);
}
#[test]
fn test_elapsed_ms_returns_milliseconds() {
let mut vm = create_test_vm();
let args = [make_instant_arg(std::time::Instant::now())];
let result = v2_elapsed_ms(&mut vm, &args, None).unwrap();
assert_eq!(result.kind, NativeKind::Float64);
let ms = result.as_f64().unwrap();
assert!(ms >= 0.0);
assert!(ms < 1000.0);
}
#[test]
fn test_elapsed_us_returns_microseconds() {
let mut vm = create_test_vm();
let args = [make_instant_arg(std::time::Instant::now())];
let result = v2_elapsed_us(&mut vm, &args, None).unwrap();
assert_eq!(result.kind, NativeKind::Float64);
let us = result.as_f64().unwrap();
assert!(us >= 0.0);
assert!(us < 1_000_000.0);
}
#[test]
fn test_elapsed_ns_returns_int() {
let mut vm = create_test_vm();
let args = [make_instant_arg(std::time::Instant::now())];
let result = v2_elapsed_ns(&mut vm, &args, None).unwrap();
assert_eq!(result.kind, NativeKind::Int64);
let ns = result.as_i64().unwrap();
assert!(ns >= 0);
}
#[test]
fn test_to_string_format() {
let mut vm = create_test_vm();
let args = [make_instant_arg(std::time::Instant::now())];
let result = v2_to_string(&mut vm, &args, None).unwrap();
assert_eq!(result.kind, NativeKind::String);
let s = result.as_str().unwrap();
assert!(s.starts_with("Instant(elapsed:"));
assert!(s.ends_with("s)"));
}
#[test]
fn test_duration_since() {
let mut vm = create_test_vm();
let earlier = std::time::Instant::now();
std::hint::black_box(0u64.wrapping_add(1));
let later = std::time::Instant::now();
let args = [make_instant_arg(later), make_instant_arg(earlier)];
let result = v2_duration_since(&mut vm, &args, None).unwrap();
assert_eq!(result.kind, NativeKind::Float64);
let ms = result.as_f64().unwrap();
assert!(ms >= 0.0);
}
#[test]
fn test_recv_instant_wrong_kind_errors() {
let mut vm = create_test_vm();
let args = [KindedSlot::from_int(42)];
let err = v2_elapsed(&mut vm, &args, None).unwrap_err();
match err {
VMError::RuntimeError(msg) => assert!(msg.contains("expected Instant receiver")),
other => panic!("expected RuntimeError, got {:?}", other),
}
}
}