|
| 1 | +use super::util::{IntoException, IntoExceptionResultExt, TypeError, ValueResult}; |
| 2 | +use num_traits::ToPrimitive; |
| 3 | +use spacetimedb_sats::{i256, u256}; |
| 4 | + |
| 5 | +pub(super) trait FromValue: Sized { |
| 6 | + fn from_value<'s>(scope: &mut v8::HandleScope<'s>, val: v8::Local<'_, v8::Value>) -> ValueResult<'s, Self>; |
| 7 | +} |
| 8 | + |
| 9 | +impl FromValue for bool { |
| 10 | + fn from_value<'s>(scope: &mut v8::HandleScope<'s>, val: v8::Local<'_, v8::Value>) -> ValueResult<'s, Self> { |
| 11 | + let b = cast!(val, v8::Boolean, "boolean").map_err(|e| e.into_exception(scope))?; |
| 12 | + Ok(b.is_true()) |
| 13 | + } |
| 14 | +} |
| 15 | + |
| 16 | +macro_rules! cast { |
| 17 | + ($val:expr, $t:ty, $expected:literal $(, $args:expr)* $(,)?) => {{ |
| 18 | + let val = $val; |
| 19 | + val.try_cast::<$t>() |
| 20 | + .map_err(|_| $crate::host::v8::util::TypeError(format!( |
| 21 | + concat!("Expected ", $expected, ", got {__got}"), |
| 22 | + $($args,)* |
| 23 | + __got = val.type_repr() |
| 24 | + ))) |
| 25 | + }}; |
| 26 | +} |
| 27 | +pub(super) use cast; |
| 28 | + |
| 29 | +macro_rules! num_from_value { |
| 30 | + ($($t:ident: $to:ident),*) => { |
| 31 | + $(impl FromValue for $t { |
| 32 | + fn from_value<'s>(scope: &mut v8::HandleScope<'s>, val: v8::Local<'_, v8::Value>) -> ValueResult<'s, Self> { |
| 33 | + let num = cast!(val, v8::Number, "number for {}", stringify!($t)).map_err_exc(scope)?; |
| 34 | + num.value() |
| 35 | + .$to() |
| 36 | + .ok_or_else(|| TypeError(format!("Value overflowed {}", stringify!($t)))) |
| 37 | + .map_err_exc(scope) |
| 38 | + } |
| 39 | + })* |
| 40 | + }; |
| 41 | + (64bit $($t:ident: $value_method:ident),*) => { |
| 42 | + $(impl FromValue for $t { |
| 43 | + fn from_value<'s>(scope: &mut v8::HandleScope<'s>, val: v8::Local<'_, v8::Value>) -> ValueResult<'s, Self> { |
| 44 | + let int = cast!(val, v8::BigInt, "bigint for {}", stringify!($t)).map_err_exc(scope)?; |
| 45 | + let (val, ok) = int.$value_method(); |
| 46 | + ok.then_some(val) |
| 47 | + .ok_or_else(|| TypeError(format!("Value overflowed {}", stringify!($t)))) |
| 48 | + .map_err_exc(scope) |
| 49 | + } |
| 50 | + })* |
| 51 | + }; |
| 52 | + (float $($t:ident),*) => { |
| 53 | + $(impl FromValue for $t { |
| 54 | + fn from_value<'s>(scope: &mut v8::HandleScope<'s>, val: v8::Local<'_, v8::Value>) -> ValueResult<'s, Self> { |
| 55 | + let num = cast!(val, v8::Number, "number for {}", stringify!($t)).map_err_exc(scope)?; |
| 56 | + Ok(num.value() as _) |
| 57 | + } |
| 58 | + })* |
| 59 | + }; |
| 60 | + (large $($t:ident: $value64:ident),*) => { |
| 61 | + $(impl FromValue for $t { |
| 62 | + fn from_value<'s>(scope: &mut v8::HandleScope<'s>, val: v8::Local<'_, v8::Value>) -> ValueResult<'s, Self> { |
| 63 | + let int = cast!(val, v8::BigInt, "bigint for {}", stringify!($t)).map_err_exc(scope)?; |
| 64 | + if let (val, true) = int.u64_value() { |
| 65 | + return Ok(val.into()); |
| 66 | + } |
| 67 | + const WORDS: usize = size_of::<$t>() / size_of::<u64>(); |
| 68 | + let mut err = || TypeError(format!("Value overflowed {}", stringify!($t))).into_exception(scope); |
| 69 | + if int.word_count() > WORDS { |
| 70 | + #[allow(unused_comparisons)] |
| 71 | + if $t::MIN < 0 && int.word_count() == WORDS + 1 { |
| 72 | + let mut words = [0u64; WORDS + 1]; |
| 73 | + let (sign, _) = int.to_words_array(&mut words); |
| 74 | + let [prev @ .., last] = words; |
| 75 | + if sign && prev == [0; WORDS] && last == (1 << 63) { |
| 76 | + return Ok($t::MIN) |
| 77 | + } |
| 78 | + } |
| 79 | + return Err(err()); |
| 80 | + } |
| 81 | + let mut words = [0u64; WORDS]; |
| 82 | + let (sign, _) = int.to_words_array(&mut words); |
| 83 | + let bytes = bytemuck::must_cast(words.map(|w| w.to_le_bytes())); |
| 84 | + let x = Self::from_le_bytes(bytes); |
| 85 | + if sign { |
| 86 | + x.checked_neg().ok_or_else(err) |
| 87 | + } else { |
| 88 | + Ok(x) |
| 89 | + } |
| 90 | + } |
| 91 | + })* |
| 92 | + }; |
| 93 | +} |
| 94 | + |
| 95 | +num_from_value!(u8: to_u8, i8: to_i8, u16: to_u16, i16: to_i16, u32: to_u32, i32: to_i32); |
| 96 | + |
| 97 | +num_from_value!(64bit u64: u64_value, i64: i64_value); |
| 98 | + |
| 99 | +num_from_value!(float f32, f64); |
| 100 | + |
| 101 | +num_from_value!(large u128: u64_value, i128: i64_value, u256: u64_value, i256: i64_value); |
| 102 | + |
| 103 | +pub(super) trait ToValue { |
| 104 | + fn to_value<'s>(&self, scope: &mut v8::HandleScope<'s>) -> ValueResult<'s, v8::Local<'s, v8::Value>>; |
| 105 | +} |
| 106 | + |
| 107 | +impl ToValue for bool { |
| 108 | + fn to_value<'s>(&self, scope: &mut v8::HandleScope<'s>) -> ValueResult<'s, v8::Local<'s, v8::Value>> { |
| 109 | + Ok(v8::Boolean::new(scope, *self).into()) |
| 110 | + } |
| 111 | +} |
| 112 | + |
| 113 | +macro_rules! num_to_value { |
| 114 | + ($($t:ident),*) => { |
| 115 | + $(impl ToValue for $t { |
| 116 | + fn to_value<'s>(&self, scope: &mut v8::HandleScope<'s>) -> ValueResult<'s, v8::Local<'s, v8::Value>> { |
| 117 | + Ok(v8::Number::new(scope, *self as f64).into()) |
| 118 | + } |
| 119 | + })* |
| 120 | + }; |
| 121 | + (64bit $($t:ident: $new_from:ident),*) => { |
| 122 | + $(impl ToValue for $t { |
| 123 | + fn to_value<'s>(&self, scope: &mut v8::HandleScope<'s>) -> ValueResult<'s, v8::Local<'s, v8::Value>> { |
| 124 | + Ok(v8::BigInt::$new_from(scope, *self).into()) |
| 125 | + } |
| 126 | + })* |
| 127 | + }; |
| 128 | + (large $($t:ident),*) => { |
| 129 | + $(impl ToValue for $t { |
| 130 | + fn to_value<'s>(&self, scope: &mut v8::HandleScope<'s>) -> ValueResult<'s, v8::Local<'s, v8::Value>> { |
| 131 | + const WORDS: usize = size_of::<$t>() / size_of::<u64>(); |
| 132 | + #[allow(unused_comparisons)] |
| 133 | + let sign = *self < 0; |
| 134 | + let Some(magnitude) = (if sign { self.checked_neg() } else { Some(*self) }) else { |
| 135 | + let mut words = [0u64; WORDS + 1]; |
| 136 | + let [.., last] = &mut words; |
| 137 | + *last = 1 << 63; |
| 138 | + return Ok(v8::BigInt::new_from_words(scope, true, &words).unwrap().into()); |
| 139 | + }; |
| 140 | + let bytes = magnitude.to_le_bytes(); |
| 141 | + let words = bytemuck::must_cast::<_, [u64; WORDS]>(bytes).map(u64::from_le); |
| 142 | + Ok(v8::BigInt::new_from_words(scope, sign, &words).unwrap().into()) |
| 143 | + } |
| 144 | + })* |
| 145 | + }; |
| 146 | +} |
| 147 | + |
| 148 | +num_to_value!(u8, i8, u16, i16, u32, i32, f32, f64); |
| 149 | + |
| 150 | +num_to_value!(64bit u64: new_from_u64, i64: new_from_i64); |
| 151 | + |
| 152 | +num_to_value!(large u128, i128, u256, i256); |
0 commit comments