use std::rc::Rc;
use super::value::JsValue;
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct NanBoxedValue(u64);
const QNAN: u64 = 0x7FF8_0000_0000_0000;
const TAG_SHIFT: u64 = 48;
const PAYLOAD_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
const TAG_UNDEFINED: u64 = 0;
const TAG_NULL: u64 = 1;
const TAG_BOOLEAN: u64 = 2;
const TAG_SMI: u64 = 3;
const TAG_HEAP_PTR: u64 = 4;
const TAG_STRING: u64 = 5;
const TAG_SYMBOL: u64 = 6;
const TAG_THE_HOLE: u64 = 7;
impl NanBoxedValue {
#[inline(always)]
#[must_use]
pub fn from_double(v: f64) -> Self {
Self(v.to_bits())
}
#[inline(always)]
#[must_use]
pub fn undefined() -> Self {
Self(QNAN | (TAG_UNDEFINED << TAG_SHIFT))
}
#[inline(always)]
#[must_use]
pub fn null() -> Self {
Self(QNAN | (TAG_NULL << TAG_SHIFT))
}
#[inline(always)]
#[must_use]
pub fn from_boolean(v: bool) -> Self {
Self(QNAN | (TAG_BOOLEAN << TAG_SHIFT) | u64::from(v))
}
#[inline(always)]
#[must_use]
pub fn from_smi(v: i32) -> Self {
Self(QNAN | (TAG_SMI << TAG_SHIFT) | (u32::from_ne_bytes(v.to_ne_bytes()) as u64))
}
#[inline(always)]
#[must_use]
pub fn the_hole() -> Self {
Self(QNAN | (TAG_THE_HOLE << TAG_SHIFT))
}
#[inline(always)]
#[must_use]
pub fn from_symbol(id: u64) -> Self {
Self(QNAN | (TAG_SYMBOL << TAG_SHIFT) | (id & PAYLOAD_MASK))
}
#[inline(always)]
#[must_use]
pub unsafe fn from_heap_ptr(ptr: *mut u8) -> Self {
let addr = ptr as u64;
debug_assert!(addr & !PAYLOAD_MASK == 0, "pointer exceeds 48 bits");
Self(QNAN | (TAG_HEAP_PTR << TAG_SHIFT) | (addr & PAYLOAD_MASK))
}
#[inline(always)]
#[must_use]
pub unsafe fn from_string_ptr(ptr: *const str) -> Self {
let addr = ptr as *const () as u64;
debug_assert!(addr & !PAYLOAD_MASK == 0, "string pointer exceeds 48 bits");
Self(QNAN | (TAG_STRING << TAG_SHIFT) | (addr & PAYLOAD_MASK))
}
#[inline(always)]
#[must_use]
pub fn is_double(self) -> bool {
(self.0 & QNAN) != QNAN
}
#[inline(always)]
#[must_use]
fn tag(self) -> u64 {
(self.0 >> TAG_SHIFT) & 0x7
}
#[inline(always)]
#[must_use]
fn payload(self) -> u64 {
self.0 & PAYLOAD_MASK
}
#[inline(always)]
#[must_use]
pub fn is_undefined(self) -> bool {
!self.is_double() && self.tag() == TAG_UNDEFINED
}
#[inline(always)]
#[must_use]
pub fn is_null(self) -> bool {
!self.is_double() && self.tag() == TAG_NULL
}
#[inline(always)]
#[must_use]
pub fn is_boolean(self) -> bool {
!self.is_double() && self.tag() == TAG_BOOLEAN
}
#[inline(always)]
#[must_use]
pub fn is_smi(self) -> bool {
!self.is_double() && self.tag() == TAG_SMI
}
#[inline(always)]
#[must_use]
pub fn is_heap_ptr(self) -> bool {
!self.is_double() && self.tag() == TAG_HEAP_PTR
}
#[inline(always)]
#[must_use]
pub fn is_string(self) -> bool {
!self.is_double() && self.tag() == TAG_STRING
}
#[inline(always)]
#[must_use]
pub fn is_symbol(self) -> bool {
!self.is_double() && self.tag() == TAG_SYMBOL
}
#[inline(always)]
#[must_use]
pub fn is_the_hole(self) -> bool {
!self.is_double() && self.tag() == TAG_THE_HOLE
}
#[inline(always)]
#[must_use]
pub fn as_double(self) -> f64 {
f64::from_bits(self.0)
}
#[inline(always)]
#[must_use]
pub fn as_boolean(self) -> bool {
self.payload() != 0
}
#[inline(always)]
#[must_use]
pub fn as_smi(self) -> i32 {
self.payload() as u32 as i32
}
#[inline(always)]
#[must_use]
pub fn as_symbol(self) -> u64 {
self.payload()
}
#[inline(always)]
#[must_use]
pub unsafe fn as_heap_ptr(self) -> *mut u8 {
self.payload() as *mut u8
}
#[inline(always)]
#[must_use]
pub fn to_bits(self) -> u64 {
self.0
}
#[inline(always)]
#[must_use]
pub fn from_bits(bits: u64) -> Self {
Self(bits)
}
#[must_use]
pub fn pack(value: JsValue) -> Self {
match value {
JsValue::Undefined => Self::undefined(),
JsValue::Null => Self::null(),
JsValue::TheHole => Self::the_hole(),
JsValue::Boolean(b) => Self::from_boolean(b),
JsValue::Smi(i) => Self::from_smi(i),
JsValue::HeapNumber(f) => {
if f.to_bits() & QNAN == QNAN {
let boxed = Box::new(JsValue::HeapNumber(f));
let ptr = Box::into_raw(boxed) as *mut u8;
unsafe { Self::from_heap_ptr(ptr) }
} else {
Self::from_double(f)
}
}
JsValue::Symbol(id) => Self::from_symbol(id),
JsValue::String(s) => {
let boxed: Box<Rc<str>> = Box::new(s);
let addr = Box::into_raw(boxed) as u64;
debug_assert!(
addr & !PAYLOAD_MASK == 0,
"string box pointer exceeds 48 bits"
);
Self(QNAN | (TAG_STRING << TAG_SHIFT) | (addr & PAYLOAD_MASK))
}
other => {
let boxed = Box::new(other);
let ptr = Box::into_raw(boxed) as *mut u8;
unsafe { Self::from_heap_ptr(ptr) }
}
}
}
pub unsafe fn unpack(self) -> JsValue {
if self.is_double() {
return JsValue::HeapNumber(self.as_double());
}
match self.tag() {
TAG_UNDEFINED => JsValue::Undefined,
TAG_NULL => JsValue::Null,
TAG_BOOLEAN => JsValue::Boolean(self.as_boolean()),
TAG_SMI => JsValue::Smi(self.as_smi()),
TAG_THE_HOLE => JsValue::TheHole,
TAG_SYMBOL => JsValue::Symbol(self.as_symbol()),
TAG_STRING => {
let ptr = self.payload() as *mut Rc<str>;
JsValue::String(*unsafe { Box::from_raw(ptr) })
}
TAG_HEAP_PTR => {
let ptr = self.payload() as *mut JsValue;
*unsafe { Box::from_raw(ptr) }
}
_ => unreachable!(),
}
}
pub unsafe fn clone_value(&self) -> JsValue {
if self.is_double() {
return JsValue::HeapNumber(self.as_double());
}
match self.tag() {
TAG_UNDEFINED => JsValue::Undefined,
TAG_NULL => JsValue::Null,
TAG_BOOLEAN => JsValue::Boolean(self.as_boolean()),
TAG_SMI => JsValue::Smi(self.as_smi()),
TAG_THE_HOLE => JsValue::TheHole,
TAG_SYMBOL => JsValue::Symbol(self.as_symbol()),
TAG_STRING => {
let ptr = self.payload() as *const Rc<str>;
JsValue::String(unsafe { (*ptr).clone() })
}
TAG_HEAP_PTR => {
let ptr = self.payload() as *const JsValue;
unsafe { (*ptr).clone() }
}
_ => unreachable!(),
}
}
#[inline(always)]
pub fn smi_add(self, other: Self) -> Option<Self> {
if self.is_smi() && other.is_smi() {
self.as_smi()
.checked_add(other.as_smi())
.map(Self::from_smi)
} else {
None
}
}
#[inline(always)]
pub fn smi_sub(self, other: Self) -> Option<Self> {
if self.is_smi() && other.is_smi() {
self.as_smi()
.checked_sub(other.as_smi())
.map(Self::from_smi)
} else {
None
}
}
#[inline(always)]
pub fn smi_mul(self, other: Self) -> Option<Self> {
if self.is_smi() && other.is_smi() {
self.as_smi()
.checked_mul(other.as_smi())
.map(Self::from_smi)
} else {
None
}
}
#[inline(always)]
pub fn smi_less_than(self, other: Self) -> Option<Self> {
if self.is_smi() && other.is_smi() {
Some(Self::from_boolean(self.as_smi() < other.as_smi()))
} else {
None
}
}
#[inline(always)]
pub fn smi_greater_than(self, other: Self) -> Option<Self> {
if self.is_smi() && other.is_smi() {
Some(Self::from_boolean(self.as_smi() > other.as_smi()))
} else {
None
}
}
#[inline(always)]
pub fn smi_equal(self, other: Self) -> Option<Self> {
if self.is_smi() && other.is_smi() {
Some(Self::from_boolean(self.as_smi() == other.as_smi()))
} else {
None
}
}
}
impl std::fmt::Debug for NanBoxedValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_double() {
write!(f, "NanBoxed(double: {})", self.as_double())
} else {
match self.tag() {
TAG_UNDEFINED => write!(f, "NanBoxed(undefined)"),
TAG_NULL => write!(f, "NanBoxed(null)"),
TAG_BOOLEAN => write!(f, "NanBoxed(bool: {})", self.as_boolean()),
TAG_SMI => write!(f, "NanBoxed(smi: {})", self.as_smi()),
TAG_HEAP_PTR => write!(f, "NanBoxed(heap: 0x{:012x})", self.payload()),
TAG_STRING => write!(f, "NanBoxed(string: 0x{:012x})", self.payload()),
TAG_SYMBOL => write!(f, "NanBoxed(symbol: {})", self.as_symbol()),
TAG_THE_HOLE => write!(f, "NanBoxed(the_hole)"),
_ => write!(f, "NanBoxed(unknown tag: {})", self.tag()),
}
}
}
}
impl PartialEq for NanBoxedValue {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for NanBoxedValue {}
#[cfg(test)]
mod tests {
use super::*;
use crate::objects::value::JsValue;
use std::rc::Rc;
#[test]
fn test_undefined() {
let v = NanBoxedValue::undefined();
assert!(v.is_undefined());
assert!(!v.is_null());
assert!(!v.is_double());
assert!(!v.is_smi());
}
#[test]
fn test_null() {
let v = NanBoxedValue::null();
assert!(v.is_null());
assert!(!v.is_undefined());
}
#[test]
fn test_boolean_true() {
let v = NanBoxedValue::from_boolean(true);
assert!(v.is_boolean());
assert!(v.as_boolean());
}
#[test]
fn test_boolean_false() {
let v = NanBoxedValue::from_boolean(false);
assert!(v.is_boolean());
assert!(!v.as_boolean());
}
#[test]
fn test_smi_positive() {
let v = NanBoxedValue::from_smi(42);
assert!(v.is_smi());
assert_eq!(v.as_smi(), 42);
}
#[test]
fn test_smi_negative() {
let v = NanBoxedValue::from_smi(-1);
assert!(v.is_smi());
assert_eq!(v.as_smi(), -1);
}
#[test]
fn test_smi_zero() {
let v = NanBoxedValue::from_smi(0);
assert!(v.is_smi());
assert_eq!(v.as_smi(), 0);
}
#[test]
fn test_smi_max() {
let v = NanBoxedValue::from_smi(i32::MAX);
assert!(v.is_smi());
assert_eq!(v.as_smi(), i32::MAX);
}
#[test]
fn test_smi_min() {
let v = NanBoxedValue::from_smi(i32::MIN);
assert!(v.is_smi());
assert_eq!(v.as_smi(), i32::MIN);
}
#[test]
fn test_double_normal() {
let v = NanBoxedValue::from_double(3.14);
assert!(v.is_double());
assert!(!v.is_smi());
assert_eq!(v.as_double(), 3.14);
}
#[test]
fn test_double_zero() {
let v = NanBoxedValue::from_double(0.0);
assert!(v.is_double());
assert_eq!(v.as_double(), 0.0);
}
#[test]
fn test_double_negative_zero() {
let v = NanBoxedValue::from_double(-0.0);
assert!(v.is_double());
assert!(v.as_double().is_sign_negative());
}
#[test]
fn test_double_infinity() {
let v = NanBoxedValue::from_double(f64::INFINITY);
assert!(v.is_double());
assert!(v.as_double().is_infinite());
}
#[test]
fn test_double_negative_infinity() {
let v = NanBoxedValue::from_double(f64::NEG_INFINITY);
assert!(v.is_double());
assert!(v.as_double().is_infinite());
assert!(v.as_double().is_sign_negative());
}
#[test]
fn test_the_hole() {
let v = NanBoxedValue::the_hole();
assert!(v.is_the_hole());
assert!(!v.is_undefined());
}
#[test]
fn test_symbol() {
let v = NanBoxedValue::from_symbol(12345);
assert!(v.is_symbol());
assert_eq!(v.as_symbol(), 12345);
}
#[test]
fn test_equality() {
assert_eq!(NanBoxedValue::undefined(), NanBoxedValue::undefined());
assert_eq!(NanBoxedValue::null(), NanBoxedValue::null());
assert_ne!(NanBoxedValue::undefined(), NanBoxedValue::null());
assert_eq!(NanBoxedValue::from_smi(42), NanBoxedValue::from_smi(42));
assert_ne!(NanBoxedValue::from_smi(1), NanBoxedValue::from_smi(2));
}
#[test]
fn test_size() {
assert_eq!(std::mem::size_of::<NanBoxedValue>(), 8);
}
#[test]
fn test_copy_semantics() {
let a = NanBoxedValue::from_smi(99);
let b = a;
assert_eq!(a.as_smi(), b.as_smi());
}
#[test]
fn test_round_trip_bits() {
let v = NanBoxedValue::from_smi(42);
let bits = v.to_bits();
let v2 = NanBoxedValue::from_bits(bits);
assert_eq!(v, v2);
}
#[test]
fn test_heap_ptr_round_trip() {
let data: Box<u64> = Box::new(42);
let ptr = Box::into_raw(data) as *mut u8;
let v = unsafe { NanBoxedValue::from_heap_ptr(ptr) };
assert!(v.is_heap_ptr());
let recovered = unsafe { v.as_heap_ptr() };
assert_eq!(ptr, recovered);
unsafe {
drop(Box::from_raw(recovered as *mut u64));
}
}
#[test]
fn test_double_nan_is_double() {
let v = NanBoxedValue::from_double(f64::NAN);
let bits = v.to_bits();
assert_eq!(bits, f64::NAN.to_bits());
}
#[test]
fn test_pack_unpack_undefined() {
let packed = NanBoxedValue::pack(JsValue::Undefined);
assert!(packed.is_undefined());
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::Undefined));
}
#[test]
fn test_pack_unpack_null() {
let packed = NanBoxedValue::pack(JsValue::Null);
assert!(packed.is_null());
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::Null));
}
#[test]
fn test_pack_unpack_the_hole() {
let packed = NanBoxedValue::pack(JsValue::TheHole);
assert!(packed.is_the_hole());
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::TheHole));
}
#[test]
fn test_pack_unpack_boolean_true() {
let packed = NanBoxedValue::pack(JsValue::Boolean(true));
assert!(packed.is_boolean());
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::Boolean(true)));
}
#[test]
fn test_pack_unpack_boolean_false() {
let packed = NanBoxedValue::pack(JsValue::Boolean(false));
assert!(packed.is_boolean());
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::Boolean(false)));
}
#[test]
fn test_pack_unpack_smi_positive() {
let packed = NanBoxedValue::pack(JsValue::Smi(42));
assert!(packed.is_smi());
assert_eq!(packed.as_smi(), 42);
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::Smi(42)));
}
#[test]
fn test_pack_unpack_smi_negative() {
let packed = NanBoxedValue::pack(JsValue::Smi(-1));
assert!(packed.is_smi());
assert_eq!(packed.as_smi(), -1);
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::Smi(-1)));
}
#[test]
fn test_pack_unpack_smi_max() {
let packed = NanBoxedValue::pack(JsValue::Smi(i32::MAX));
assert_eq!(packed.as_smi(), i32::MAX);
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::Smi(i32::MAX)));
}
#[test]
fn test_pack_unpack_smi_min() {
let packed = NanBoxedValue::pack(JsValue::Smi(i32::MIN));
assert_eq!(packed.as_smi(), i32::MIN);
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::Smi(i32::MIN)));
}
#[test]
fn test_pack_unpack_heap_number() {
let packed = NanBoxedValue::pack(JsValue::HeapNumber(3.14));
assert!(packed.is_double());
assert_eq!(packed.as_double(), 3.14);
let unpacked = unsafe { packed.unpack() };
match unpacked {
JsValue::HeapNumber(f) => assert_eq!(f, 3.14),
_ => panic!("expected HeapNumber"),
}
}
#[test]
fn test_pack_unpack_negative_zero() {
let packed = NanBoxedValue::pack(JsValue::HeapNumber(-0.0));
assert!(packed.is_double());
let unpacked = unsafe { packed.unpack() };
match unpacked {
JsValue::HeapNumber(f) => {
assert_eq!(f, 0.0);
assert!(f.is_sign_negative());
}
_ => panic!("expected HeapNumber"),
}
}
#[test]
fn test_pack_unpack_infinity() {
let packed = NanBoxedValue::pack(JsValue::HeapNumber(f64::INFINITY));
assert!(packed.is_double());
let unpacked = unsafe { packed.unpack() };
match unpacked {
JsValue::HeapNumber(f) => assert!(f.is_infinite() && f.is_sign_positive()),
_ => panic!("expected HeapNumber"),
}
}
#[test]
fn test_pack_unpack_neg_infinity() {
let packed = NanBoxedValue::pack(JsValue::HeapNumber(f64::NEG_INFINITY));
let unpacked = unsafe { packed.unpack() };
match unpacked {
JsValue::HeapNumber(f) => assert!(f.is_infinite() && f.is_sign_negative()),
_ => panic!("expected HeapNumber"),
}
}
#[test]
fn test_pack_unpack_nan() {
let packed = NanBoxedValue::pack(JsValue::HeapNumber(f64::NAN));
assert!(packed.is_heap_ptr());
let unpacked = unsafe { packed.unpack() };
match unpacked {
JsValue::HeapNumber(f) => assert!(f.is_nan()),
_ => panic!("expected HeapNumber(NaN)"),
}
}
#[test]
fn test_pack_unpack_symbol() {
let packed = NanBoxedValue::pack(JsValue::Symbol(12345));
assert!(packed.is_symbol());
assert_eq!(packed.as_symbol(), 12345);
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::Symbol(12345)));
}
#[test]
fn test_pack_unpack_string() {
let s: Rc<str> = Rc::from("hello");
let packed = NanBoxedValue::pack(JsValue::String(s));
assert!(packed.is_string());
let unpacked = unsafe { packed.unpack() };
match unpacked {
JsValue::String(s) => assert_eq!(&*s, "hello"),
_ => panic!("expected String"),
}
}
#[test]
fn test_pack_clone_value_string_refcount() {
let s: Rc<str> = Rc::from("hello");
assert_eq!(Rc::strong_count(&s), 1);
let packed = NanBoxedValue::pack(JsValue::String(s));
let cloned = unsafe { packed.clone_value() };
match &cloned {
JsValue::String(s) => {
assert_eq!(&**s, "hello");
assert_eq!(Rc::strong_count(s), 2);
}
_ => panic!("expected String"),
}
drop(cloned);
let unpacked = unsafe { packed.unpack() };
match unpacked {
JsValue::String(s) => {
assert_eq!(&*s, "hello");
assert_eq!(Rc::strong_count(&s), 1);
}
_ => panic!("expected String"),
}
}
#[test]
fn test_pack_unpack_bigint() {
let packed = NanBoxedValue::pack(JsValue::BigInt(Box::new(42)));
assert!(packed.is_heap_ptr());
let unpacked = unsafe { packed.unpack() };
assert!(matches!(unpacked, JsValue::BigInt(b) if *b == 42));
}
#[test]
fn test_clone_value_primitives() {
let cases = [
NanBoxedValue::pack(JsValue::Undefined),
NanBoxedValue::pack(JsValue::Null),
NanBoxedValue::pack(JsValue::TheHole),
NanBoxedValue::pack(JsValue::Boolean(true)),
NanBoxedValue::pack(JsValue::Smi(99)),
NanBoxedValue::pack(JsValue::HeapNumber(2.718)),
NanBoxedValue::pack(JsValue::Symbol(7)),
];
for packed in &cases {
let cloned = unsafe { packed.clone_value() };
drop(cloned);
}
}
#[test]
fn test_smi_add_basic() {
let a = NanBoxedValue::from_smi(10);
let b = NanBoxedValue::from_smi(20);
let result = a.smi_add(b).unwrap();
assert_eq!(result.as_smi(), 30);
}
#[test]
fn test_smi_add_negative() {
let a = NanBoxedValue::from_smi(-5);
let b = NanBoxedValue::from_smi(3);
let result = a.smi_add(b).unwrap();
assert_eq!(result.as_smi(), -2);
}
#[test]
fn test_smi_add_overflow() {
let a = NanBoxedValue::from_smi(i32::MAX);
let b = NanBoxedValue::from_smi(1);
assert!(a.smi_add(b).is_none());
}
#[test]
fn test_smi_add_non_smi() {
let a = NanBoxedValue::from_smi(1);
let b = NanBoxedValue::from_double(2.0);
assert!(a.smi_add(b).is_none());
}
#[test]
fn test_smi_sub_basic() {
let a = NanBoxedValue::from_smi(30);
let b = NanBoxedValue::from_smi(10);
let result = a.smi_sub(b).unwrap();
assert_eq!(result.as_smi(), 20);
}
#[test]
fn test_smi_sub_overflow() {
let a = NanBoxedValue::from_smi(i32::MIN);
let b = NanBoxedValue::from_smi(1);
assert!(a.smi_sub(b).is_none());
}
#[test]
fn test_smi_mul_basic() {
let a = NanBoxedValue::from_smi(6);
let b = NanBoxedValue::from_smi(7);
let result = a.smi_mul(b).unwrap();
assert_eq!(result.as_smi(), 42);
}
#[test]
fn test_smi_mul_overflow() {
let a = NanBoxedValue::from_smi(i32::MAX);
let b = NanBoxedValue::from_smi(2);
assert!(a.smi_mul(b).is_none());
}
#[test]
fn test_smi_less_than() {
let a = NanBoxedValue::from_smi(1);
let b = NanBoxedValue::from_smi(2);
let result = a.smi_less_than(b).unwrap();
assert!(result.as_boolean());
let result = b.smi_less_than(a).unwrap();
assert!(!result.as_boolean());
}
#[test]
fn test_smi_greater_than() {
let a = NanBoxedValue::from_smi(5);
let b = NanBoxedValue::from_smi(3);
let result = a.smi_greater_than(b).unwrap();
assert!(result.as_boolean());
}
#[test]
fn test_smi_equal_values() {
let a = NanBoxedValue::from_smi(42);
let b = NanBoxedValue::from_smi(42);
let c = NanBoxedValue::from_smi(99);
assert!(a.smi_equal(b).unwrap().as_boolean());
assert!(!a.smi_equal(c).unwrap().as_boolean());
}
#[test]
fn test_smi_comparison_non_smi() {
let a = NanBoxedValue::from_smi(1);
let b = NanBoxedValue::undefined();
assert!(a.smi_less_than(b).is_none());
assert!(a.smi_greater_than(b).is_none());
assert!(a.smi_equal(b).is_none());
}
}