Skip to main content

deno_core/
convert.rs

1// Copyright 2018-2026 the Deno authors. MIT license.
2
3use std::convert::Infallible;
4use std::mem::MaybeUninit;
5use std::ops::Deref;
6use std::ops::DerefMut;
7
8use deno_error::JsErrorBox;
9use deno_error::JsErrorClass;
10use smallvec::SmallVec;
11use v8::Local;
12use v8::PinScope;
13
14use crate::error::DataError;
15use crate::runtime::ops;
16
17/// A conversion from a rust value to a v8 value.
18///
19/// When passing data from Rust into JS, either
20/// via an op or by calling a JS function directly,
21/// you need to serialize the data into a native
22/// V8 value. When using the [`op2`][deno_core::op2] macro, the return
23/// value is converted to a `v8::Local<Value>` automatically,
24/// and the strategy for conversion is controlled by attributes
25/// like `#[smi]`, `#[number]`, `#[string]`. For types with support
26/// built-in to the op2 macro, like primitives, strings, and buffers,
27/// these attributes are sufficient and you don't need to worry about this trait.
28///
29/// If, however, you want to return a custom type from an op, or
30/// simply want more control over the conversion process,
31/// you can implement the `ToV8` trait. This allows you the
32/// choose the best serialization strategy for your specific use case.
33/// You can then use the `#[to_v8]` attribute to indicate
34/// that the `#[op2]` macro should call your implementation for the conversion.
35///
36/// # Example
37///
38/// ```ignore
39/// use deno_core::ToV8;
40/// use deno_core::convert::Smi;
41/// use deno_core::op2;
42///
43/// struct Foo(i32);
44///
45/// impl<'a> ToV8<'a> for Foo {
46///   // This conversion can never fail, so we use `Infallible` as the error type.
47///   // Any error type that implements `std::error::Error` can be used here.
48///   type Error = std::convert::Infallible;
49///
50///   fn to_v8(self, scope: &mut v8::PinScope<'a, '_>) -> Result<v8::Local<'a, v8::Value>, Self::Error> {
51///     // For performance, pass this value as a `v8::Integer` (i.e. a `smi`).
52///     // The `Smi` wrapper type implements this conversion for you.
53///     Smi(self.0).to_v8(scope)
54///   }
55/// }
56///
57/// // using the `#[to_v8]` attribute tells the `op2` macro to call this implementation.
58/// #[op2]
59/// #[to_v8]
60/// fn op_foo() -> Foo {
61///   Foo(42)
62/// }
63/// ```
64///
65/// # Performance Notes
66/// ## Structs
67/// The natural representation of a struct in JS is an object with fields
68/// corresponding the struct. This, however, is a performance footgun and
69/// you should avoid creating and passing objects to V8 whenever possible.
70/// In general, if you need to pass a compound type to JS, it is more performant to serialize
71/// to a tuple (a `v8::Array`) rather than an object.
72/// Object keys are V8 strings, and strings are expensive to pass to V8
73/// and they have to be managed by the V8 garbage collector.
74/// Tuples, on the other hand, are keyed by `smi`s, which are immediates
75/// and don't require allocation or garbage collection.
76pub trait ToV8<'a> {
77  type Error: JsErrorClass;
78
79  /// Converts the value to a V8 value.
80  fn to_v8<'i>(
81    self,
82    scope: &mut v8::PinScope<'a, 'i>,
83  ) -> Result<v8::Local<'a, v8::Value>, Self::Error>;
84}
85
86/// A conversion from a v8 value to a rust value.
87///
88/// When writing a op, or otherwise writing a function in Rust called
89/// from JS, arguments passed from JS are represented as [`v8::Local<v8::Value>>`][deno_core::v8::Value].
90/// To convert these values into custom Rust types, you can implement the [`FromV8`] trait.
91///
92/// Once you've implemented this trait, you can use the `#[from_v8]` attribute
93/// to tell the [`op2`][deno_core::op2] macro to use your implementation to convert the argument
94/// to the desired type.
95///
96/// # Example
97///
98/// ```ignore
99/// use deno_core::FromV8;
100/// use deno_error::JsErrorBox;
101/// use deno_core::convert::Smi;
102/// use deno_core::op2;
103///
104/// struct Foo(i32);
105///
106/// impl<'a> FromV8<'a> for Foo {
107///   // This conversion can fail, so we use `JsErrorBox` as the error type.
108///   // Any error type that implements `std::error::Error` can be used here.
109///   type Error = JsErrorBox;
110///
111///   fn from_v8(scope: &mut v8::PinScope<'a, '_>, value: v8::Local<'a, v8::Value>) -> Result<Self, Self::Error> {
112///     /// We expect this value to be a `v8::Integer`, so we use the [`Smi`][deno_core::convert::Smi] wrapper type to convert it.
113///     Smi::from_v8(scope, value).map(|Smi(v)| Foo(v))
114///   }
115/// }
116///
117/// // using the `#[from_v8]` attribute tells the `op2` macro to call this implementation.
118/// #[op2]
119/// fn op_foo(#[from_v8] foo: Foo) {
120///   let Foo(_) = foo;
121/// }
122/// ```
123pub trait FromV8<'a>: Sized {
124  type Error: JsErrorClass;
125
126  /// Converts a V8 value to a Rust value.
127  fn from_v8<'i>(
128    scope: &mut v8::PinScope<'a, 'i>,
129    value: v8::Local<'a, v8::Value>,
130  ) -> Result<Self, Self::Error>;
131}
132
133/// An alternative to [`FromV8`] that does not require a [`PinScope`].
134pub trait FromV8Scopeless<'a>: Sized + FromV8<'a> {
135  /// Converts a V8 value to a Rust value.
136  fn from_v8(value: v8::Local<'a, v8::Value>) -> Result<Self, Self::Error>;
137}
138
139// impls
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
142/// Marks a numeric type as being serialized as a v8 `smi` in a `v8::Integer`.
143#[repr(transparent)]
144pub struct Smi<T: SmallInt>(pub T);
145
146/// A trait for types that can represent a JS `smi`.
147pub trait SmallInt {
148  const NAME: &'static str;
149
150  #[allow(
151    clippy::wrong_self_convention,
152    reason = "takes self by value intentionally"
153  )]
154  fn as_i32(self) -> i32;
155  fn from_i32(value: i32) -> Self;
156}
157
158macro_rules! impl_smallint {
159  (for $($t:ty),*) => {
160    $(
161      impl SmallInt for $t {
162        const NAME: &'static str = stringify!($t);
163        #[allow(clippy::wrong_self_convention, reason = "takes self by value intentionally")]
164        #[inline(always)]
165        fn as_i32(self) -> i32 {
166          self as _
167        }
168
169        #[inline(always)]
170        fn from_i32(value: i32) -> Self {
171            value as _
172        }
173      }
174    )*
175  };
176}
177
178impl_smallint!(for u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
179
180impl<'s, T: SmallInt> ToV8<'s> for Smi<T> {
181  type Error = Infallible;
182
183  #[inline]
184  fn to_v8<'i>(
185    self,
186    scope: &mut v8::PinScope<'s, 'i>,
187  ) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
188    Ok(v8::Integer::new(scope, self.0.as_i32()).into())
189  }
190}
191
192impl<'s, T: SmallInt> FromV8<'s> for Smi<T> {
193  type Error = DataError;
194
195  fn from_v8<'i>(
196    _scope: &mut PinScope<'s, 'i>,
197    value: Local<'s, v8::Value>,
198  ) -> Result<Self, Self::Error> {
199    <Self as FromV8Scopeless>::from_v8(value)
200  }
201}
202
203impl<'s, T: SmallInt> FromV8Scopeless<'s> for Smi<T> {
204  #[inline]
205  fn from_v8(value: v8::Local<'s, v8::Value>) -> Result<Self, Self::Error> {
206    let v = ops::to_i32_option(&value).ok_or_else(|| {
207      DataError(v8::DataError::BadType {
208        actual: value.type_repr(),
209        expected: T::NAME,
210      })
211    })?;
212    Ok(Smi(T::from_i32(v)))
213  }
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
217/// Marks a numeric type as being serialized as a v8 `number` in a `v8::Number`.
218#[repr(transparent)]
219pub struct Number<T: Numeric>(pub T);
220
221/// A trait for types that can represent a JS `number`.
222pub trait Numeric: Sized {
223  const NAME: &'static str;
224  #[allow(
225    clippy::wrong_self_convention,
226    reason = "takes self by value intentionally"
227  )]
228  fn as_f64(self) -> f64;
229  fn from_value(value: &v8::Value) -> Option<Self>;
230}
231
232macro_rules! impl_numeric {
233  ($($t:ty : $from: path ),*) => {
234    $(
235      impl Numeric for $t {
236        const NAME: &'static str = stringify!($t);
237        #[inline(always)]
238        fn from_value(value: &v8::Value) -> Option<Self> {
239          $from(value).map(|v| v as _)
240        }
241
242        #[allow(clippy::wrong_self_convention, reason = "takes self by value intentionally")]
243        #[inline(always)]
244        fn as_f64(self) -> f64 {
245            self as _
246        }
247      }
248    )*
249  };
250}
251
252impl_numeric!(
253  f32   : ops::to_f32_option,
254  f64   : ops::to_f64_option,
255  u32   : ops::to_u32_option,
256  u64   : ops::to_u64_option,
257  usize : ops::to_u64_option,
258  i32   : ops::to_i32_option,
259  i64   : ops::to_i64_option,
260  isize : ops::to_i64_option
261);
262
263impl<'s, T: Numeric> ToV8<'s> for Number<T> {
264  type Error = Infallible;
265  #[inline]
266  fn to_v8<'i>(
267    self,
268    scope: &mut v8::PinScope<'s, 'i>,
269  ) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
270    Ok(v8::Number::new(scope, self.0.as_f64()).into())
271  }
272}
273
274impl<'s, T: Numeric> FromV8<'s> for Number<T> {
275  type Error = DataError;
276
277  fn from_v8<'i>(
278    _scope: &mut PinScope<'s, 'i>,
279    value: Local<'s, v8::Value>,
280  ) -> Result<Self, Self::Error> {
281    <Self as FromV8Scopeless>::from_v8(value)
282  }
283}
284
285impl<'s, T: Numeric> FromV8Scopeless<'s> for Number<T> {
286  #[inline]
287  fn from_v8(value: v8::Local<'s, v8::Value>) -> Result<Self, Self::Error> {
288    T::from_value(&value).map(Number).ok_or_else(|| {
289      DataError(v8::DataError::BadType {
290        actual: value.type_repr(),
291        expected: T::NAME,
292      })
293    })
294  }
295}
296
297macro_rules! impl_number_types {
298  ($($t:ty),*) => {
299    $(
300      impl<'a> FromV8<'a> for $t {
301        type Error = DataError;
302        #[inline]
303        fn from_v8<'i>(
304          _scope: &mut v8::PinScope<'a, 'i>,
305          value: v8::Local<'a, v8::Value>,
306        ) -> Result<Self, Self::Error> {
307          <Self as FromV8Scopeless>::from_v8(value)
308        }
309      }
310      impl<'a> FromV8Scopeless<'a> for $t {
311        #[inline]
312        fn from_v8(value: v8::Local<'a, v8::Value>) -> Result<Self, Self::Error> {
313          let n = value.try_cast::<v8::Number>()?;
314          Ok(n.value() as Self)
315        }
316      }
317
318      impl<'a> ToV8<'a> for $t {
319        type Error = Infallible;
320        #[inline]
321        fn to_v8<'i>(
322          self,
323          scope: &mut v8::PinScope<'a, 'i>,
324        ) -> Result<v8::Local<'a, v8::Value>, Self::Error> {
325          Ok(v8::Number::new(scope, self as f64).into())
326        }
327      }
328    )*
329  };
330}
331
332impl_number_types!(
333  u8, i8, u16, i16, u32, i32, u64, i64, usize, isize, f32, f64
334);
335
336#[derive(Debug)]
337pub struct BigInt {
338  pub sign_bit: bool,
339  pub words: Vec<u64>,
340}
341
342impl<'s> ToV8<'s> for BigInt {
343  type Error = JsErrorBox;
344
345  fn to_v8<'i>(
346    self,
347    scope: &mut v8::PinScope<'s, 'i>,
348  ) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
349    v8::BigInt::new_from_words(scope, self.sign_bit, &self.words)
350      .map(Into::into)
351      .ok_or_else(|| JsErrorBox::type_error("Failed to create BigInt"))
352  }
353}
354
355impl<'s> FromV8<'s> for BigInt {
356  type Error = DataError;
357
358  fn from_v8<'i>(
359    _scope: &mut v8::PinScope<'s, 'i>,
360    value: v8::Local<'s, v8::Value>,
361  ) -> Result<Self, Self::Error> {
362    let bigint = value.try_cast::<v8::BigInt>()?;
363
364    let word_count = bigint.word_count();
365    let mut words = vec![0u64; word_count];
366    let (sign_bit, _) = bigint.to_words_array(&mut words);
367
368    Ok(BigInt { sign_bit, words })
369  }
370}
371
372impl From<num_bigint::BigInt> for BigInt {
373  fn from(big_int: num_bigint::BigInt) -> Self {
374    let (sign, words) = big_int.to_u64_digits();
375    Self {
376      sign_bit: sign == num_bigint::Sign::Minus,
377      words,
378    }
379  }
380}
381
382impl From<BigInt> for num_bigint::BigInt {
383  fn from(big_int: BigInt) -> Self {
384    // SAFETY: Because the alignment of u64 is 8, the alignment of u32 is 4, and
385    // the size of u64 is 8, the size of u32 is 4, the alignment of u32 is a
386    // factor of the alignment of u64, and the size of u32 is a factor of the
387    // size of u64, we can safely transmute the slice of u64 to a slice of u32.
388    let (prefix, slice, suffix) = unsafe { big_int.words.align_to::<u32>() };
389    assert!(prefix.is_empty());
390    assert!(suffix.is_empty());
391    assert_eq!(slice.len(), big_int.words.len() * 2);
392    Self::from_slice(
393      if big_int.sign_bit {
394        num_bigint::Sign::Minus
395      } else {
396        num_bigint::Sign::Plus
397      },
398      slice,
399    )
400  }
401}
402
403impl<'s> ToV8<'s> for bool {
404  type Error = Infallible;
405  #[inline]
406  fn to_v8<'i>(
407    self,
408    scope: &mut v8::PinScope<'s, 'i>,
409  ) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
410    Ok(v8::Boolean::new(scope, self).into())
411  }
412}
413
414impl<'s> FromV8<'s> for bool {
415  type Error = DataError;
416
417  fn from_v8<'i>(
418    _scope: &mut PinScope<'s, 'i>,
419    value: Local<'s, v8::Value>,
420  ) -> Result<Self, Self::Error> {
421    <Self as FromV8Scopeless>::from_v8(value)
422  }
423}
424
425impl<'s> FromV8Scopeless<'s> for bool {
426  #[inline]
427  fn from_v8(value: v8::Local<'s, v8::Value>) -> Result<Self, Self::Error> {
428    value
429      .try_cast::<v8::Boolean>()
430      .map(|v| v.is_true())
431      .map_err(DataError)
432  }
433}
434
435impl<'s> FromV8<'s> for String {
436  type Error = Infallible;
437  #[inline]
438  fn from_v8<'i>(
439    scope: &mut v8::PinScope<'s, 'i>,
440    value: v8::Local<'s, v8::Value>,
441  ) -> Result<String, Self::Error> {
442    Ok(value.to_rust_string_lossy(scope))
443  }
444}
445impl<'s> ToV8<'s> for String {
446  type Error = Infallible;
447  #[inline]
448  fn to_v8<'i>(
449    self,
450    scope: &mut v8::PinScope<'s, 'i>,
451  ) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
452    Ok(v8::String::new(scope, &self).unwrap().into()) // TODO
453  }
454}
455
456impl<'s> ToV8<'s> for &'static str {
457  type Error = Infallible;
458  #[inline]
459  fn to_v8<'i>(
460    self,
461    scope: &mut v8::PinScope<'s, 'i>,
462  ) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
463    Ok(v8::String::new(scope, self).unwrap().into()) // TODO
464  }
465}
466
467impl<'s> ToV8<'s> for Box<str> {
468  type Error = Infallible;
469  #[inline]
470  fn to_v8<'i>(
471    self,
472    scope: &mut v8::PinScope<'s, 'i>,
473  ) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
474    Ok(v8::String::new(scope, &self).unwrap().into()) // TODO
475  }
476}
477
478const USIZE2X: usize = std::mem::size_of::<usize>() * 2;
479#[derive(PartialEq, Eq, Clone, Debug, Default)]
480pub struct ByteString(SmallVec<[u8; USIZE2X]>);
481
482impl Deref for ByteString {
483  type Target = SmallVec<[u8; USIZE2X]>;
484
485  fn deref(&self) -> &Self::Target {
486    &self.0
487  }
488}
489
490impl DerefMut for ByteString {
491  fn deref_mut(&mut self) -> &mut Self::Target {
492    &mut self.0
493  }
494}
495
496impl AsRef<[u8]> for ByteString {
497  fn as_ref(&self) -> &[u8] {
498    &self.0
499  }
500}
501
502impl AsMut<[u8]> for ByteString {
503  fn as_mut(&mut self) -> &mut [u8] {
504    &mut self.0
505  }
506}
507
508impl<'a> ToV8<'a> for ByteString {
509  type Error = Infallible;
510
511  fn to_v8<'i>(
512    self,
513    scope: &mut v8::PinScope<'a, 'i>,
514  ) -> Result<v8::Local<'a, v8::Value>, Self::Error> {
515    let v = v8::String::new_from_one_byte(
516      scope,
517      self.as_ref(),
518      v8::NewStringType::Normal,
519    )
520    .unwrap();
521    Ok(v.into())
522  }
523}
524
525#[derive(Debug, thiserror::Error, deno_error::JsError)]
526#[class(type)]
527pub enum ByteStringError {
528  #[error("Invalid type, expected: string but got: {0}")]
529  ExpectedString(&'static str),
530  #[error("Invalid type, expected: latin1")]
531  ExpectedLatin1,
532}
533
534impl<'a> FromV8<'a> for ByteString {
535  type Error = ByteStringError;
536
537  fn from_v8<'i>(
538    scope: &mut v8::PinScope<'a, 'i>,
539    value: v8::Local<'a, v8::Value>,
540  ) -> Result<Self, Self::Error> {
541    let v8str = v8::Local::<v8::String>::try_from(value)
542      .map_err(|_| ByteStringError::ExpectedString(value.type_repr()))?;
543    if !v8str.contains_only_onebyte() {
544      return Err(ByteStringError::ExpectedLatin1);
545    }
546    let len = v8str.length();
547    let mut buffer = SmallVec::with_capacity(len);
548    #[allow(
549      clippy::uninit_vec,
550      reason = "buffer is immediately written to after set_len"
551    )]
552    // SAFETY: we set length == capacity (see previous line),
553    // before immediately writing into that buffer and sanity check with an assert
554    unsafe {
555      buffer.set_len(len);
556      v8str.write_one_byte_v2(scope, 0, &mut buffer, v8::WriteFlags::empty());
557    }
558    Ok(Self(buffer))
559  }
560}
561
562impl From<Vec<u8>> for ByteString {
563  fn from(vec: Vec<u8>) -> Self {
564    ByteString(SmallVec::from_vec(vec))
565  }
566}
567
568#[allow(clippy::from_over_into, reason = "cannot implement From for Vec")]
569impl Into<Vec<u8>> for ByteString {
570  fn into(self) -> Vec<u8> {
571    self.0.into_vec()
572  }
573}
574
575impl From<&[u8]> for ByteString {
576  fn from(s: &[u8]) -> Self {
577    ByteString(SmallVec::from_slice(s))
578  }
579}
580
581impl From<&str> for ByteString {
582  fn from(s: &str) -> Self {
583    let v: Vec<u8> = s.into();
584    ByteString::from(v)
585  }
586}
587
588impl From<String> for ByteString {
589  fn from(s: String) -> Self {
590    ByteString::from(s.into_bytes())
591  }
592}
593
594#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
595/// A wrapper type for `Option<T>` that (de)serializes `None` as `null`
596#[repr(transparent)]
597pub struct OptionNull<T>(pub Option<T>);
598
599impl<T> From<Option<T>> for OptionNull<T> {
600  fn from(option: Option<T>) -> Self {
601    Self(option)
602  }
603}
604
605impl<T> From<OptionNull<T>> for Option<T> {
606  fn from(value: OptionNull<T>) -> Self {
607    value.0
608  }
609}
610
611impl<'s, T> ToV8<'s> for OptionNull<T>
612where
613  T: ToV8<'s>,
614{
615  type Error = T::Error;
616
617  fn to_v8<'i>(
618    self,
619    scope: &mut v8::PinScope<'s, 'i>,
620  ) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
621    match self.0 {
622      Some(value) => value.to_v8(scope),
623      None => Ok(v8::null(scope).into()),
624    }
625  }
626}
627
628impl<'s, T> FromV8<'s> for OptionNull<T>
629where
630  T: FromV8<'s>,
631{
632  type Error = T::Error;
633
634  fn from_v8<'i>(
635    scope: &mut v8::PinScope<'s, 'i>,
636    value: v8::Local<'s, v8::Value>,
637  ) -> Result<Self, Self::Error> {
638    if value.is_null() {
639      Ok(OptionNull(None))
640    } else {
641      T::from_v8(scope, value).map(|v| OptionNull(Some(v)))
642    }
643  }
644}
645
646impl<'s, T> FromV8Scopeless<'s> for OptionNull<T>
647where
648  T: FromV8Scopeless<'s>,
649{
650  fn from_v8(value: v8::Local<'s, v8::Value>) -> Result<Self, Self::Error> {
651    if value.is_null() {
652      Ok(OptionNull(None))
653    } else {
654      <T as FromV8Scopeless>::from_v8(value).map(|v| OptionNull(Some(v)))
655    }
656  }
657}
658
659#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
660/// A wrapper type for `Option<T>` that (de)serializes `None` as `undefined`
661#[repr(transparent)]
662pub struct OptionUndefined<T>(pub Option<T>);
663
664impl<T> From<Option<T>> for OptionUndefined<T> {
665  fn from(option: Option<T>) -> Self {
666    Self(option)
667  }
668}
669
670impl<T> From<OptionUndefined<T>> for Option<T> {
671  fn from(value: OptionUndefined<T>) -> Self {
672    value.0
673  }
674}
675
676impl<'s, T> ToV8<'s> for OptionUndefined<T>
677where
678  T: ToV8<'s>,
679{
680  type Error = T::Error;
681
682  fn to_v8<'i>(
683    self,
684    scope: &mut v8::PinScope<'s, 'i>,
685  ) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
686    match self.0 {
687      Some(value) => value.to_v8(scope),
688      None => Ok(v8::undefined(scope).into()),
689    }
690  }
691}
692
693impl<'s, T> FromV8<'s> for OptionUndefined<T>
694where
695  T: FromV8<'s>,
696{
697  type Error = T::Error;
698
699  fn from_v8<'i>(
700    scope: &mut v8::PinScope<'s, 'i>,
701    value: v8::Local<'s, v8::Value>,
702  ) -> Result<Self, Self::Error> {
703    if value.is_undefined() {
704      Ok(OptionUndefined(None))
705    } else {
706      T::from_v8(scope, value).map(|v| OptionUndefined(Some(v)))
707    }
708  }
709}
710
711impl<'s, T> FromV8Scopeless<'s> for OptionUndefined<T>
712where
713  T: FromV8Scopeless<'s>,
714{
715  fn from_v8(value: v8::Local<'s, v8::Value>) -> Result<Self, Self::Error> {
716    if value.is_undefined() {
717      Ok(OptionUndefined(None))
718    } else {
719      <T as FromV8Scopeless>::from_v8(value).map(|v| OptionUndefined(Some(v)))
720    }
721  }
722}
723
724unsafe fn abview_to_box<T>(
725  ab_view: v8::Local<v8::ArrayBufferView>,
726) -> Box<[T]> {
727  if ab_view.byte_length() == 0 {
728    return Box::new([]);
729  }
730  let data = ab_view.data();
731  let len = ab_view.byte_length() / std::mem::size_of::<T>();
732  let mut out = Box::<[T]>::new_uninit_slice(len);
733  unsafe {
734    std::ptr::copy_nonoverlapping(
735      data.cast::<T>(),
736      out.as_mut_ptr().cast::<T>(),
737      len,
738    );
739    out.assume_init()
740  }
741}
742
743macro_rules! typedarray_to_v8 {
744  ($ty:ty, $v8ty:ident, $v8fn:ident) => {
745    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
746    pub struct $v8ty(pub Box<[$ty]>);
747
748    impl std::ops::Deref for $v8ty {
749      type Target = [$ty];
750      fn deref(&self) -> &Self::Target {
751        &self.0
752      }
753    }
754
755    impl From<Vec<$ty>> for $v8ty {
756      fn from(value: Vec<$ty>) -> Self {
757        Self(value.into_boxed_slice())
758      }
759    }
760
761    impl From<Box<[$ty]>> for $v8ty {
762      fn from(value: Box<[$ty]>) -> Self {
763        Self(value)
764      }
765    }
766
767    impl<'a> ToV8<'a> for $v8ty {
768      type Error = JsErrorBox;
769
770      fn to_v8<'i>(
771        self,
772        scope: &mut v8::PinScope<'a, 'i>,
773      ) -> Result<v8::Local<'a, v8::Value>, Self::Error> {
774        let len = self.0.len();
775        if self.0.is_empty() {
776          let ab = v8::ArrayBuffer::new(scope, 0);
777          return v8::$v8ty::new(scope, ab, 0, 0)
778            .ok_or_else(|| {
779              JsErrorBox::type_error("Failed to create typed array")
780            })
781            .map(|v| v.into());
782        }
783        let backing = v8::ArrayBuffer::new_backing_store_from_bytes(self.0);
784        let backing_shared = backing.make_shared();
785        let ab = v8::ArrayBuffer::with_backing_store(scope, &backing_shared);
786        v8::$v8ty::new(scope, ab, 0, len)
787          .ok_or_else(|| JsErrorBox::type_error("Failed to create typed array"))
788          .map(|v| v.into())
789      }
790    }
791
792    impl<'s> FromV8<'s> for $v8ty {
793      type Error = DataError;
794
795      fn from_v8<'i>(
796        _scope: &mut PinScope<'s, 'i>,
797        value: Local<'s, v8::Value>,
798      ) -> Result<Self, Self::Error> {
799        <Self as FromV8Scopeless>::from_v8(value)
800      }
801    }
802
803    impl<'a> FromV8Scopeless<'a> for $v8ty {
804      fn from_v8(value: v8::Local<'a, v8::Value>) -> Result<Self, Self::Error> {
805        if value.$v8fn() {
806          Ok($v8ty(unsafe {
807            abview_to_box::<$ty>(value.cast::<v8::ArrayBufferView>())
808          }))
809        } else {
810          Err(DataError(v8::DataError::BadType {
811            actual: value.type_repr(),
812            expected: stringify!($v8ty),
813          }))
814        }
815      }
816    }
817  };
818}
819
820typedarray_to_v8!(i8, Int8Array, is_int8_array);
821typedarray_to_v8!(u8, Uint8Array, is_uint8_array);
822typedarray_to_v8!(i16, Int16Array, is_int16_array);
823typedarray_to_v8!(u16, Uint16Array, is_uint16_array);
824typedarray_to_v8!(i32, Int32Array, is_int32_array);
825typedarray_to_v8!(u32, Uint32Array, is_uint32_array);
826typedarray_to_v8!(i64, BigInt64Array, is_big_int64_array);
827typedarray_to_v8!(u64, BigUint64Array, is_big_uint64_array);
828
829pub enum ArrayBufferView {
830  Int8Array(Int8Array),
831  Uint8Array(Uint8Array),
832  Int16Array(Int16Array),
833  Uint16Array(Uint16Array),
834  Int32Array(Int32Array),
835  Uint32Array(Uint32Array),
836  BigInt64Array(BigInt64Array),
837  BigUint64Array(BigUint64Array),
838}
839
840impl<'a> ToV8<'a> for ArrayBufferView {
841  type Error = JsErrorBox;
842
843  fn to_v8<'i>(
844    self,
845    scope: &mut PinScope<'a, 'i>,
846  ) -> Result<Local<'a, v8::Value>, Self::Error> {
847    match self {
848      ArrayBufferView::Int8Array(view) => view.to_v8(scope),
849      ArrayBufferView::Uint8Array(view) => view.to_v8(scope),
850      ArrayBufferView::Int16Array(view) => view.to_v8(scope),
851      ArrayBufferView::Uint16Array(view) => view.to_v8(scope),
852      ArrayBufferView::Int32Array(view) => view.to_v8(scope),
853      ArrayBufferView::Uint32Array(view) => view.to_v8(scope),
854      ArrayBufferView::BigInt64Array(view) => view.to_v8(scope),
855      ArrayBufferView::BigUint64Array(view) => view.to_v8(scope),
856    }
857  }
858}
859
860impl<'s> FromV8<'s> for ArrayBufferView {
861  type Error = DataError;
862
863  fn from_v8<'i>(
864    _scope: &mut PinScope<'s, 'i>,
865    value: Local<'s, v8::Value>,
866  ) -> Result<Self, Self::Error> {
867    <Self as FromV8Scopeless>::from_v8(value)
868  }
869}
870
871impl<'a> FromV8Scopeless<'a> for ArrayBufferView {
872  fn from_v8(value: Local<'a, v8::Value>) -> Result<Self, Self::Error> {
873    if value.is_int8_array() {
874      Ok(Self::Int8Array(<Int8Array as FromV8Scopeless>::from_v8(
875        value,
876      )?))
877    } else if value.is_uint8_array() {
878      Ok(Self::Uint8Array(<Uint8Array as FromV8Scopeless>::from_v8(
879        value,
880      )?))
881    } else if value.is_int16_array() {
882      Ok(Self::Int16Array(<Int16Array as FromV8Scopeless>::from_v8(
883        value,
884      )?))
885    } else if value.is_uint16_array() {
886      Ok(Self::Uint16Array(
887        <Uint16Array as FromV8Scopeless>::from_v8(value)?,
888      ))
889    } else if value.is_int32_array() {
890      Ok(Self::Int32Array(<Int32Array as FromV8Scopeless>::from_v8(
891        value,
892      )?))
893    } else if value.is_uint32_array() {
894      Ok(Self::Uint32Array(
895        <Uint32Array as FromV8Scopeless>::from_v8(value)?,
896      ))
897    } else if value.is_big_int64_array() {
898      Ok(Self::BigInt64Array(
899        <BigInt64Array as FromV8Scopeless>::from_v8(value)?,
900      ))
901    } else if value.is_big_uint64_array() {
902      Ok(Self::BigUint64Array(
903        <BigUint64Array as FromV8Scopeless>::from_v8(value)?,
904      ))
905    } else {
906      Err(DataError(v8::DataError::BadType {
907        actual: value.type_repr(),
908        expected: "ArrayBufferView",
909      }))
910    }
911  }
912}
913
914impl<'a, T> ToV8<'a> for Vec<T>
915where
916  T: ToV8<'a>,
917{
918  type Error = T::Error;
919
920  fn to_v8(
921    self,
922    scope: &mut v8::PinScope<'a, '_>,
923  ) -> Result<v8::Local<'a, v8::Value>, Self::Error> {
924    let buf = self
925      .into_iter()
926      .map(|v| v.to_v8(scope))
927      .collect::<Result<Vec<_>, _>>()?;
928    Ok(v8::Array::new_with_elements(scope, &buf).into())
929  }
930}
931
932impl<'a, T> FromV8<'a> for Vec<T>
933where
934  T: FromV8<'a>,
935{
936  type Error = JsErrorBox;
937
938  fn from_v8(
939    scope: &mut v8::PinScope<'a, '_>,
940    value: v8::Local<'a, v8::Value>,
941  ) -> Result<Self, Self::Error> {
942    let arr = v8::Local::<v8::Array>::try_from(value)
943      .map_err(|e| JsErrorBox::from_err(DataError(e)))?;
944    let len = arr.length() as usize;
945
946    let mut out = maybe_uninit_vec::<T>(len);
947
948    for i in 0..len {
949      let v = arr.get_index(scope, i as u32).unwrap();
950      match T::from_v8(scope, v) {
951        Ok(v) => {
952          out[i].write(v);
953        }
954        Err(e) => {
955          // need to drop the elements we've already written
956          for elem in out.iter_mut().take(i) {
957            // SAFETY: we've initialized these elements
958            unsafe {
959              elem.assume_init_drop();
960            }
961          }
962          return Err(JsErrorBox::from_err(e));
963        }
964      }
965    }
966
967    // SAFETY: all elements have been initialized, and `MaybeUninit<T>`
968    // is transmutable to `T`
969    let out = unsafe { transmute_vec::<MaybeUninit<T>, T>(out) };
970
971    Ok(out)
972  }
973}
974
975fn maybe_uninit_vec<T>(len: usize) -> Vec<std::mem::MaybeUninit<T>> {
976  let mut v = Vec::with_capacity(len);
977  // SAFETY: `MaybeUninit` is allowed to be uninitialized and
978  // the length is the same as the capacity.
979  unsafe {
980    v.set_len(len);
981  }
982  v
983}
984
985/// Transmutes a `Vec` of one type to a `Vec` of another type.
986///
987/// # Safety
988/// `T` must be transmutable to `U`
989unsafe fn transmute_vec<T, U>(v: Vec<T>) -> Vec<U> {
990  const {
991    assert!(std::mem::size_of::<T>() == std::mem::size_of::<U>());
992    assert!(std::mem::align_of::<T>() == std::mem::align_of::<U>());
993  }
994
995  // make sure the original vector is not dropped
996  let mut v = std::mem::ManuallyDrop::new(v);
997  let len = v.len();
998  let cap = v.capacity();
999  let ptr = v.as_mut_ptr();
1000
1001  // SAFETY: the original vector is not dropped, the caller upholds the
1002  // transmutability invariants, and the length and capacity are not changed.
1003  unsafe { Vec::from_raw_parts(ptr as *mut U, len, cap) }
1004}
1005
1006macro_rules! impl_tuple {
1007  ($($len: expr; ($($name: ident),*)),+) => {
1008    $(
1009      impl<'a, $($name),+> ToV8<'a> for ($($name,)+)
1010      where
1011        $($name: ToV8<'a>,)+
1012      {
1013        type Error = deno_error::JsErrorBox;
1014        fn to_v8(self, scope: &mut v8::PinScope<'a, '_>) -> Result<v8::Local<'a, v8::Value>, Self::Error> {
1015          #[allow(non_snake_case, reason = "macro-generated bindings match type parameter names")]
1016          let ($($name,)+) = self;
1017          let elements = &[$($name.to_v8(scope).map_err(deno_error::JsErrorBox::from_err)?),+];
1018          Ok(v8::Array::new_with_elements(scope, elements).into())
1019        }
1020      }
1021      impl<'a, $($name),+> FromV8<'a> for ($($name,)+)
1022      where
1023        $($name: FromV8<'a>,)+
1024      {
1025        type Error = deno_error::JsErrorBox;
1026
1027        fn from_v8(
1028          scope: &mut v8::PinScope<'a, '_>,
1029          value: v8::Local<'a, v8::Value>,
1030        ) -> Result<Self, Self::Error> {
1031          let array = v8::Local::<v8::Array>::try_from(value)
1032            .map_err(|e| deno_error::JsErrorBox::from_err(crate::error::DataError(e)))?;
1033          if array.length() != $len {
1034            return Err(deno_error::JsErrorBox::type_error(format!("Expected {} elements, got {}", $len, array.length())));
1035          }
1036          let mut i = 0;
1037          #[allow(non_snake_case, reason = "macro-generated bindings match type parameter names")]
1038          let ($($name,)+) = (
1039            $(
1040              {
1041                let element = array.get_index(scope, i).unwrap();
1042                let res = $name::from_v8(scope, element).map_err(deno_error::JsErrorBox::from_err)?;
1043                #[allow(unused, reason = "counter is used in multi-element tuples")]
1044                {
1045                  i += 1;
1046                }
1047                res
1048              },
1049            )+
1050          );
1051          Ok(($($name,)+))
1052        }
1053      }
1054    )+
1055  };
1056}
1057
1058impl_tuple!(
1059  1; (A),
1060  2; (A, B),
1061  3; (A, B, C),
1062  4; (A, B, C, D),
1063  5; (A, B, C, D, E),
1064  6; (A, B, C, D, E, F),
1065  7; (A, B, C, D, E, F, G),
1066  8; (A, B, C, D, E, F, G, H),
1067  9; (A, B, C, D, E, F, G, H, I),
1068  10; (A, B, C, D, E, F, G, H, I, J),
1069  11; (A, B, C, D, E, F, G, H, I, J, K),
1070  12; (A, B, C, D, E, F, G, H, I, J, K, L)
1071);
1072
1073impl<'s, T> ToV8<'s> for Option<T>
1074where
1075  T: ToV8<'s>,
1076{
1077  type Error = T::Error;
1078
1079  fn to_v8<'i>(
1080    self,
1081    scope: &mut v8::PinScope<'s, 'i>,
1082  ) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
1083    match self {
1084      Some(value) => value.to_v8(scope),
1085      None => Ok(v8::null(scope).into()),
1086    }
1087  }
1088}
1089
1090impl<'s, T> FromV8<'s> for Option<T>
1091where
1092  T: FromV8<'s>,
1093{
1094  type Error = T::Error;
1095
1096  fn from_v8<'i>(
1097    scope: &mut v8::PinScope<'s, 'i>,
1098    value: v8::Local<'s, v8::Value>,
1099  ) -> Result<Self, Self::Error> {
1100    if value.is_null_or_undefined() {
1101      Ok(None)
1102    } else {
1103      T::from_v8(scope, value).map(|v| Some(v))
1104    }
1105  }
1106}
1107
1108impl<'s, T> FromV8Scopeless<'s> for Option<T>
1109where
1110  T: FromV8Scopeless<'s>,
1111{
1112  fn from_v8(value: v8::Local<'s, v8::Value>) -> Result<Self, Self::Error> {
1113    if value.is_null_or_undefined() {
1114      Ok(None)
1115    } else {
1116      <T as FromV8Scopeless>::from_v8(value).map(|v| Some(v))
1117    }
1118  }
1119}
1120
1121#[derive(Debug, thiserror::Error, deno_error::JsError)]
1122pub enum V8ConvertError {
1123  #[class(inherit)]
1124  #[error(transparent)]
1125  Infallible(#[from] Infallible),
1126  #[class(inherit)]
1127  #[error(transparent)]
1128  DataError(DataError),
1129}
1130
1131impl From<v8::DataError> for V8ConvertError {
1132  fn from(value: v8::DataError) -> Self {
1133    Self::DataError(value.into())
1134  }
1135}
1136
1137impl<'s, T, E> ToV8<'s> for v8::Global<T>
1138where
1139  Local<'s, T>: TryInto<Local<'s, v8::Value>, Error = E>,
1140  E: Into<V8ConvertError>,
1141{
1142  type Error = V8ConvertError;
1143
1144  fn to_v8<'i>(
1145    self,
1146    scope: &mut PinScope<'s, 'i>,
1147  ) -> Result<Local<'s, v8::Value>, Self::Error> {
1148    let local: Local<'s, T> = Local::new(scope, self);
1149    local.try_into().map_err(Into::into)
1150  }
1151}
1152
1153impl<'s, T, E> FromV8<'s> for v8::Global<T>
1154where
1155  Local<'s, v8::Value>: TryInto<Local<'s, T>, Error = E>,
1156  E: Into<V8ConvertError>,
1157{
1158  type Error = V8ConvertError;
1159
1160  fn from_v8<'i>(
1161    scope: &mut PinScope<'s, 'i>,
1162    value: Local<'s, v8::Value>,
1163  ) -> Result<Self, Self::Error> {
1164    Ok(v8::Global::new(
1165      scope,
1166      value.try_into().map_err(Into::into)?,
1167    ))
1168  }
1169}
1170
1171impl<'s, T, E> ToV8<'s> for v8::Local<'s, T>
1172where
1173  Local<'s, T>: TryInto<Local<'s, v8::Value>, Error = E>,
1174  E: Into<V8ConvertError>,
1175{
1176  type Error = V8ConvertError;
1177
1178  fn to_v8<'i>(
1179    self,
1180    _scope: &mut PinScope<'s, 'i>,
1181  ) -> Result<Local<'s, v8::Value>, Self::Error> {
1182    self.try_into().map_err(Into::into)
1183  }
1184}
1185
1186impl<'s, T, E> FromV8<'s> for v8::Local<'s, T>
1187where
1188  Local<'s, v8::Value>: TryInto<Local<'s, T>, Error = E>,
1189  E: Into<V8ConvertError>,
1190{
1191  type Error = V8ConvertError;
1192
1193  fn from_v8<'i>(
1194    _scope: &mut PinScope<'s, 'i>,
1195    value: Local<'s, v8::Value>,
1196  ) -> Result<Self, Self::Error> {
1197    <Self as FromV8Scopeless>::from_v8(value)
1198  }
1199}
1200
1201impl<'s, T, E> FromV8Scopeless<'s> for v8::Local<'s, T>
1202where
1203  Local<'s, v8::Value>: TryInto<Local<'s, T>, Error = E>,
1204  E: Into<V8ConvertError>,
1205{
1206  fn from_v8(value: Local<'s, v8::Value>) -> Result<Self, Self::Error> {
1207    value.try_into().map_err(Into::into)
1208  }
1209}
1210
1211#[cfg(all(test, not(miri)))]
1212mod tests {
1213  use std::collections::HashMap;
1214  use std::sync::atomic::AtomicUsize;
1215  use std::sync::atomic::Ordering;
1216
1217  use deno_error::JsErrorClass;
1218  use v8::Local;
1219
1220  use super::*;
1221  use crate::JsRuntime;
1222  use crate::scope as scope_macro;
1223
1224  static ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
1225
1226  fn next_id() -> usize {
1227    ID_COUNTER.fetch_add(1, Ordering::Relaxed)
1228  }
1229
1230  // minified vendored code from @std/assert
1231  static ASSERT_CODE: &str = r#"
1232function equal(a, b) {
1233  if (a === b) return true;
1234  if (typeof a !== typeof b) return false;
1235  if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => equal(v, b[i]));
1236  if (typeof a === 'object' && typeof b === 'object') {
1237    const keysA = Object.keys(a);
1238    const keysB = Object.keys(b);
1239    if (keysA.length !== keysB.length) return false;
1240    for (const key of keysA) {
1241      if (!equal(a[key], b[key])) return false;
1242    }
1243    return true;
1244  }
1245  return false;
1246}
1247"#;
1248
1249  fn cast_closure<F>(f: F) -> F
1250  where
1251    F: for<'a, 'b> Fn(
1252        &mut v8::PinScope<'a, 'b>,
1253        v8::FunctionCallbackArguments<'a>,
1254        v8::ReturnValue<'a>,
1255      ) + 'static,
1256  {
1257    f
1258  }
1259
1260  fn key<'a>(
1261    scope: &mut v8::PinScope<'a, '_>,
1262    name: &str,
1263  ) -> v8::Local<'a, v8::Value> {
1264    v8::String::new(scope, name).unwrap().into()
1265  }
1266
1267  macro_rules! make_test_fn {
1268    ($scope:expr, $f:expr) => {
1269      let global = $scope.get_current_context().global($scope);
1270      let test_fn_key = key($scope, "test_fn");
1271      let test_fn = v8::FunctionBuilder::<v8::Function>::new(cast_closure($f))
1272        .build($scope)
1273        .unwrap();
1274      global.set($scope, test_fn_key, test_fn.into()).unwrap();
1275    };
1276  }
1277
1278  macro_rules! to_v8_test {
1279    ($runtime:ident, |$scope: ident, $args: ident| $to_v8:expr, $assertion:expr) => {{
1280      scope_macro!($scope, &mut $runtime);
1281      make_test_fn!($scope, |$scope, $args, mut rv| {
1282        let v = $to_v8;
1283        rv.set(v);
1284      });
1285    }
1286    {
1287      let test_name = format!("test_{}", next_id());
1288      let assertion = format!("{}{};", ASSERT_CODE, $assertion);
1289      let result = $runtime.execute_script(test_name, assertion).unwrap();
1290      scope_macro!(scope, &mut $runtime);
1291      let local = v8::Local::new(scope, result);
1292      assert!(local.is_true());
1293    }};
1294  }
1295
1296  macro_rules! from_v8_test {
1297    ($runtime:ident, $js:expr, |$scope: ident, $result: ident| $assertion:expr) => {{
1298      let js = format!("{}{};", ASSERT_CODE, $js);
1299      let $result = $runtime
1300        .execute_script(format!("test_{}", next_id()), js)
1301        .unwrap();
1302      scope_macro!($scope, &mut $runtime);
1303      let $result = v8::Local::new($scope, $result);
1304      $assertion;
1305    }};
1306  }
1307
1308  #[test]
1309  fn test_option_undefined() {
1310    let mut runtime = JsRuntime::new(Default::default());
1311
1312    to_v8_test!(
1313      runtime,
1314      |scope, _args| OptionUndefined::<Number<f64>>(None).to_v8(scope).unwrap(),
1315      "test_fn() === undefined"
1316    );
1317    to_v8_test!(
1318      runtime,
1319      |scope, _args| OptionUndefined::<Number<f64>>(Some(Number(1.0)))
1320        .to_v8(scope)
1321        .unwrap(),
1322      "test_fn() === 1.0"
1323    );
1324    from_v8_test!(runtime, "undefined", |scope, result| {
1325      let r = <OptionUndefined<Number<f64>> as FromV8>::from_v8(scope, result)
1326        .unwrap();
1327      assert_eq!(r, OptionUndefined(None));
1328    });
1329
1330    from_v8_test!(runtime, "1.0", |scope, result| {
1331      assert_eq!(
1332        <OptionUndefined::<Number<f64>> as FromV8>::from_v8(scope, result)
1333          .unwrap(),
1334        OptionUndefined(Some(Number(1.0)))
1335      )
1336    });
1337  }
1338
1339  #[test]
1340  fn test_option_null() {
1341    let mut runtime = JsRuntime::new(Default::default());
1342
1343    to_v8_test!(
1344      runtime,
1345      |scope, _args| OptionNull::<Number<f64>>(None).to_v8(scope).unwrap(),
1346      "test_fn() === null"
1347    );
1348
1349    to_v8_test!(
1350      runtime,
1351      |scope, _args| {
1352        OptionNull::<Number<f64>>(Some(Number(1.0)))
1353          .to_v8(scope)
1354          .unwrap()
1355      },
1356      "test_fn() === 1.0"
1357    );
1358
1359    from_v8_test!(runtime, "null", |scope, result| assert_eq!(
1360      <OptionNull::<Number<f64>> as FromV8>::from_v8(scope, result).unwrap(),
1361      OptionNull(None)
1362    ));
1363
1364    from_v8_test!(runtime, "1.0", |scope, result| assert_eq!(
1365      <OptionNull::<Number<f64>> as FromV8>::from_v8(scope, result).unwrap(),
1366      OptionNull(Some(Number(1.0)))
1367    ));
1368  }
1369
1370  #[test]
1371  fn test_tuple() {
1372    let mut runtime = JsRuntime::new(Default::default());
1373
1374    to_v8_test!(
1375      runtime,
1376      |scope, _args| (Number(1.0), Number(2.0)).to_v8(scope).unwrap(),
1377      "var result = test_fn(); equal(result, [1.0, 2.0])"
1378    );
1379
1380    to_v8_test!(
1381      runtime,
1382      |scope, _args| (Number(1.0), true).to_v8(scope).unwrap(),
1383      "var result = test_fn(); equal(result, [1.0, true])"
1384    );
1385
1386    to_v8_test!(
1387      runtime,
1388      |scope, _args| (
1389        Number(1.0),
1390        Number(2.0),
1391        OptionNull::<Number<f64>>(Some(Number(3.0))),
1392        OptionUndefined::<bool>(None)
1393      )
1394        .to_v8(scope)
1395        .unwrap(),
1396      "equal(test_fn(), [1.0, 2.0, 3.0, undefined])"
1397    );
1398
1399    from_v8_test!(runtime, "[1.0, 2.0]", |scope, result| {
1400      assert_eq!(
1401        <(Number<f64>, Number<f64>)>::from_v8(scope, result).unwrap(),
1402        (Number(1.0), Number(2.0))
1403      )
1404    });
1405
1406    from_v8_test!(runtime, "[1.0, 2.0, 3.0, undefined]", |scope, result| {
1407      assert_eq!(
1408        <(
1409          Number<f64>,
1410          Number<f64>,
1411          OptionNull::<Number<f64>>,
1412          OptionUndefined::<bool>
1413        )>::from_v8(scope, result)
1414        .unwrap(),
1415        (
1416          Number(1.0),
1417          Number(2.0),
1418          OptionNull(Some(Number(3.0))),
1419          OptionUndefined(None)
1420        )
1421      )
1422    });
1423
1424    from_v8_test!(runtime, "[1.0]", |scope, result| {
1425      let err = <(Number<f64>, bool)>::from_v8(scope, result).unwrap_err();
1426      assert!(
1427        err.to_string().contains("Expected 2 elements"),
1428        "expected length mismatch error, got: {}",
1429        err
1430      );
1431    });
1432  }
1433
1434  #[test]
1435  fn test_vec() {
1436    let mut runtime = JsRuntime::new(Default::default());
1437
1438    to_v8_test!(
1439      runtime,
1440      |scope, _args| vec![Number(1.0), Number(2.0)].to_v8(scope).unwrap(),
1441      "equal(test_fn(), [1.0, 2.0])"
1442    );
1443
1444    from_v8_test!(runtime, "[1.0, 2.0]", |scope, result| {
1445      assert_eq!(
1446        <Vec<Number<f64>>>::from_v8(scope, result).unwrap(),
1447        vec![Number(1.0), Number(2.0)]
1448      )
1449    });
1450
1451    to_v8_test!(
1452      runtime,
1453      |scope, _args| Vec::<Number<f64>>::new().to_v8(scope).unwrap(),
1454      "equal(test_fn(), [])"
1455    );
1456
1457    from_v8_test!(runtime, "[]", |scope, result| {
1458      let v = <Vec<Number<f64>>>::from_v8(scope, result).unwrap();
1459      assert_eq!(v, vec![]);
1460    });
1461
1462    // Test with nested Vec
1463    to_v8_test!(
1464      runtime,
1465      |scope, _args| vec![vec![Number(1.0), Number(2.0)], vec![Number(3.0)]]
1466        .to_v8(scope)
1467        .unwrap(),
1468      "equal(test_fn(), [[1.0,2.0],[3.0]])"
1469    );
1470    from_v8_test!(runtime, "[[1.0,2.0],[3.0]]", |scope, result| {
1471      let v = <Vec<Vec<Number<f64>>>>::from_v8(scope, result).unwrap();
1472      assert_eq!(v, vec![vec![Number(1.0), Number(2.0)], vec![Number(3.0)]]);
1473    });
1474
1475    // Test Vec<Option<T>>
1476    to_v8_test!(
1477      runtime,
1478      |scope, _args| vec![Some(Number(1.0)), None, Some(Number(2.0))]
1479        .to_v8(scope)
1480        .unwrap(),
1481      "equal(test_fn(), [1.0, null, 2.0])"
1482    );
1483    from_v8_test!(runtime, "[1.0, undefined, 2.0]", |scope, result| {
1484      let v = <Vec<Option<Number<f64>>>>::from_v8(scope, result).unwrap();
1485      assert_eq!(v, vec![Some(Number(1.0)), None, Some(Number(2.0))]);
1486    });
1487
1488    // Test failure case: element conversion error
1489    from_v8_test!(runtime, "[1.0, 'notanumber']", |scope, result| {
1490      let err = <Vec<Number<f64>>>::from_v8(scope, result).unwrap_err();
1491      let err = err.get_ref().downcast_ref::<DataError>().unwrap();
1492      assert_eq!(
1493        err,
1494        &DataError(v8::DataError::BadType {
1495          actual: "string",
1496          expected: "f64",
1497        })
1498      );
1499    });
1500  }
1501
1502  #[test]
1503  fn test_uint8array() {
1504    let mut runtime = JsRuntime::new(Default::default());
1505
1506    to_v8_test!(
1507      runtime,
1508      |scope, _args| Uint8Array::from(vec![1, 2, 3]).to_v8(scope).unwrap(),
1509      "equal(test_fn(), new Uint8Array([1, 2, 3]))"
1510    );
1511
1512    to_v8_test!(
1513      runtime,
1514      |scope, _args| Uint8Array::from(Vec::<u8>::new()).to_v8(scope).unwrap(),
1515      "equal(test_fn(), new Uint8Array([]))"
1516    );
1517
1518    from_v8_test!(runtime, "new Uint8Array([1, 2, 3])", |scope, result| {
1519      assert_eq!(
1520        *<Uint8Array as FromV8>::from_v8(scope, result).unwrap(),
1521        vec![1u8, 2, 3]
1522      )
1523    });
1524
1525    from_v8_test!(runtime, "new Uint8Array([])", |scope, result| {
1526      assert_eq!(
1527        *<Uint8Array as FromV8>::from_v8(scope, result).unwrap(),
1528        Vec::<u8>::new()
1529      )
1530    });
1531
1532    from_v8_test!(
1533      runtime,
1534      "new Uint8Array([1, 2, 3, 4, 5]).subarray(2)",
1535      |scope, result| {
1536        assert_eq!(
1537          *<Uint8Array as FromV8>::from_v8(scope, result).unwrap(),
1538          vec![3u8, 4, 5]
1539        )
1540      }
1541    );
1542
1543    from_v8_test!(
1544      runtime,
1545      "new Uint8Array([1, 2, 3, 4, 5]).subarray(1, 4)",
1546      |scope, result| {
1547        assert_eq!(
1548          *<Uint8Array as FromV8>::from_v8(scope, result).unwrap(),
1549          vec![2u8, 3, 4]
1550        )
1551      }
1552    );
1553
1554    from_v8_test!(
1555      runtime,
1556      "new Uint8Array([1, 2, 3]).subarray(2, 2)",
1557      |scope, result| {
1558        assert_eq!(
1559          *<Uint8Array as FromV8>::from_v8(scope, result).unwrap(),
1560          Vec::<u8>::new()
1561        )
1562      }
1563    );
1564  }
1565
1566  #[test]
1567  fn test_uint16array() {
1568    let mut runtime = JsRuntime::new(Default::default());
1569
1570    to_v8_test!(
1571      runtime,
1572      |scope, _args| Uint16Array::from(vec![1, 2, 3]).to_v8(scope).unwrap(),
1573      "equal(test_fn(), new Uint16Array([1, 2, 3]))"
1574    );
1575
1576    from_v8_test!(runtime, "new Uint16Array([1, 2, 3])", |scope, result| {
1577      assert_eq!(
1578        *<Uint16Array as FromV8>::from_v8(scope, result).unwrap(),
1579        vec![1u16, 2, 3]
1580      )
1581    });
1582
1583    from_v8_test!(runtime, "new Uint16Array([])", |scope, result| {
1584      assert_eq!(
1585        *<Uint16Array as FromV8>::from_v8(scope, result).unwrap(),
1586        Vec::<u16>::new()
1587      )
1588    });
1589
1590    from_v8_test!(
1591      runtime,
1592      "new Uint16Array([1, 2, 3, 4, 5]).subarray(2)",
1593      |scope, result| {
1594        assert_eq!(
1595          *<Uint16Array as FromV8>::from_v8(scope, result).unwrap(),
1596          vec![3u16, 4, 5]
1597        )
1598      }
1599    );
1600
1601    from_v8_test!(
1602      runtime,
1603      "new Uint16Array([1, 2, 3, 4, 5]).subarray(1, 4)",
1604      |scope, result| {
1605        assert_eq!(
1606          *<Uint16Array as FromV8>::from_v8(scope, result).unwrap(),
1607          vec![2u16, 3, 4]
1608        )
1609      }
1610    );
1611  }
1612
1613  #[test]
1614  fn test_uint32array() {
1615    let mut runtime = JsRuntime::new(Default::default());
1616
1617    to_v8_test!(
1618      runtime,
1619      |scope, _args| Uint32Array::from(vec![1, 2, 3]).to_v8(scope).unwrap(),
1620      "equal(test_fn(), new Uint32Array([1, 2, 3]))"
1621    );
1622
1623    from_v8_test!(runtime, "new Uint32Array([1, 2, 3])", |scope, result| {
1624      assert_eq!(
1625        *<Uint32Array as FromV8>::from_v8(scope, result).unwrap(),
1626        vec![1u32, 2, 3]
1627      )
1628    });
1629
1630    from_v8_test!(runtime, "new Uint32Array([])", |scope, result| {
1631      assert_eq!(
1632        *<Uint32Array as FromV8>::from_v8(scope, result).unwrap(),
1633        Vec::<u32>::new()
1634      )
1635    });
1636
1637    from_v8_test!(
1638      runtime,
1639      "new Uint32Array([1, 2, 3, 4, 5]).subarray(2)",
1640      |scope, result| {
1641        assert_eq!(
1642          *<Uint32Array as FromV8>::from_v8(scope, result).unwrap(),
1643          vec![3u32, 4, 5]
1644        )
1645      }
1646    );
1647
1648    from_v8_test!(
1649      runtime,
1650      "new Uint32Array([1, 2, 3, 4, 5]).subarray(1, 4)",
1651      |scope, result| {
1652        assert_eq!(
1653          *<Uint32Array as FromV8>::from_v8(scope, result).unwrap(),
1654          vec![2u32, 3, 4]
1655        )
1656      }
1657    );
1658  }
1659
1660  #[test]
1661  fn test_int32array() {
1662    let mut runtime = JsRuntime::new(Default::default());
1663
1664    to_v8_test!(
1665      runtime,
1666      |scope, _args| Int32Array::from(vec![1, 2, 3]).to_v8(scope).unwrap(),
1667      "equal(test_fn(), new Int32Array([1, 2, 3]))"
1668    );
1669
1670    from_v8_test!(runtime, "new Int32Array([1, 2, 3])", |scope, result| {
1671      assert_eq!(
1672        *<Int32Array as FromV8>::from_v8(scope, result).unwrap(),
1673        vec![1, 2, 3]
1674      )
1675    });
1676
1677    from_v8_test!(runtime, "new Int32Array([])", |scope, result| {
1678      assert_eq!(
1679        *<Int32Array as FromV8>::from_v8(scope, result).unwrap(),
1680        Vec::<i32>::new()
1681      )
1682    });
1683
1684    from_v8_test!(
1685      runtime,
1686      "new Int32Array([1, 2, 3, 4, 5]).subarray(2)",
1687      |scope, result| {
1688        assert_eq!(
1689          *<Int32Array as FromV8>::from_v8(scope, result).unwrap(),
1690          vec![3i32, 4, 5]
1691        )
1692      }
1693    );
1694
1695    from_v8_test!(
1696      runtime,
1697      "new Int32Array([-1, -2, -3, -4, -5]).subarray(1, 4)",
1698      |scope, result| {
1699        assert_eq!(
1700          *<Int32Array as FromV8>::from_v8(scope, result).unwrap(),
1701          vec![-2i32, -3, -4]
1702        )
1703      }
1704    );
1705  }
1706
1707  #[test]
1708  fn test_biguint64array() {
1709    let mut runtime = JsRuntime::new(Default::default());
1710
1711    to_v8_test!(
1712      runtime,
1713      |scope, _args| BigUint64Array::from(vec![1, 2, 3]).to_v8(scope).unwrap(),
1714      "equal(test_fn(), new BigUint64Array([1n, 2n, 3n]))"
1715    );
1716
1717    from_v8_test!(
1718      runtime,
1719      "new BigUint64Array([1n, 2n, 3n])",
1720      |scope, result| {
1721        assert_eq!(
1722          *<BigUint64Array as FromV8>::from_v8(scope, result).unwrap(),
1723          vec![1u64, 2, 3]
1724        )
1725      }
1726    );
1727
1728    from_v8_test!(
1729      runtime,
1730      "new BigUint64Array([1n, 2n, 3n, 4n, 5n]).subarray(2)",
1731      |scope, result| {
1732        assert_eq!(
1733          *<BigUint64Array as FromV8>::from_v8(scope, result).unwrap(),
1734          vec![3u64, 4, 5]
1735        )
1736      }
1737    );
1738
1739    from_v8_test!(
1740      runtime,
1741      "new BigUint64Array([1n, 2n, 3n, 4n, 5n]).subarray(1, 4)",
1742      |scope, result| {
1743        assert_eq!(
1744          *<BigUint64Array as FromV8>::from_v8(scope, result).unwrap(),
1745          vec![2u64, 3, 4]
1746        )
1747      }
1748    );
1749  }
1750
1751  #[test]
1752  fn test_bigint64array() {
1753    let mut runtime = JsRuntime::new(Default::default());
1754
1755    to_v8_test!(
1756      runtime,
1757      |scope, _args| BigInt64Array::from(vec![1, 2, 3]).to_v8(scope).unwrap(),
1758      "equal(test_fn(), new BigInt64Array([1n, 2n, 3n]))"
1759    );
1760
1761    from_v8_test!(
1762      runtime,
1763      "new BigInt64Array([1n, 2n, 3n])",
1764      |scope, result| {
1765        assert_eq!(
1766          *<BigInt64Array as FromV8>::from_v8(scope, result).unwrap(),
1767          vec![1, 2, 3]
1768        )
1769      }
1770    );
1771
1772    from_v8_test!(runtime, "new BigInt64Array([])", |scope, result| {
1773      assert_eq!(
1774        *<BigInt64Array as FromV8>::from_v8(scope, result).unwrap(),
1775        Vec::<i64>::new()
1776      )
1777    });
1778
1779    from_v8_test!(
1780      runtime,
1781      "new BigInt64Array([1n, 2n, 3n, 4n, 5n]).subarray(2)",
1782      |scope, result| {
1783        assert_eq!(
1784          *<BigInt64Array as FromV8>::from_v8(scope, result).unwrap(),
1785          vec![3i64, 4, 5]
1786        )
1787      }
1788    );
1789
1790    from_v8_test!(
1791      runtime,
1792      "new BigInt64Array([-1n, -2n, -3n, -4n, -5n]).subarray(1, 4)",
1793      |scope, result| {
1794        assert_eq!(
1795          *<BigInt64Array as FromV8>::from_v8(scope, result).unwrap(),
1796          vec![-2i64, -3, -4]
1797        )
1798      }
1799    );
1800  }
1801
1802  #[test]
1803  fn derive_struct() {
1804    #[derive(deno_ops::FromV8, deno_ops::ToV8, Eq, PartialEq, Clone, Debug)]
1805    pub struct Struct {
1806      a: u8,
1807      #[from_v8(default = Some(3))]
1808      c: Option<u32>,
1809      #[v8(rename = "e")]
1810      f: String,
1811      #[v8(serde)]
1812      b: HashMap<String, u32>,
1813    }
1814
1815    let mut runtime = JsRuntime::new(Default::default());
1816    deno_core::scope!(scope, runtime);
1817
1818    let value = Struct {
1819      a: 242,
1820      c: Some(102),
1821      f: "foo".to_string(),
1822      b: Default::default(),
1823    };
1824    let to = ToV8::to_v8(value.clone(), scope).unwrap();
1825    let from = FromV8::from_v8(scope, to).unwrap();
1826    assert_eq!(value, from);
1827  }
1828
1829  #[test]
1830  fn derive_from_struct() {
1831    #[derive(deno_ops::FromV8, Eq, PartialEq, Clone, Debug)]
1832    pub struct Struct {
1833      a: u8,
1834      #[from_v8(default = Some(3))]
1835      c: Option<u32>,
1836      #[v8(rename = "e")]
1837      f: String,
1838      #[v8(serde)]
1839      b: HashMap<String, u32>,
1840    }
1841
1842    let mut runtime = JsRuntime::new(Default::default());
1843    let val = runtime
1844      .execute_script("", "({ a: 1, c: 70000, e: 'foo', b: { 'bar': 1 } })")
1845      .unwrap();
1846
1847    let val2 = runtime
1848      .execute_script("", "({ a: 1, e: 'foo', b: {} })")
1849      .unwrap();
1850
1851    deno_core::scope!(scope, runtime);
1852
1853    let val = Local::new(scope, val);
1854    let from = Struct::from_v8(scope, val).unwrap();
1855    assert_eq!(
1856      from,
1857      Struct {
1858        a: 1,
1859        c: Some(70000),
1860        f: "foo".to_string(),
1861        b: HashMap::from([("bar".to_string(), 1)]),
1862      }
1863    );
1864
1865    let val2 = Local::new(scope, val2);
1866    let from = Struct::from_v8(scope, val2).unwrap();
1867    assert_eq!(
1868      from,
1869      Struct {
1870        a: 1,
1871        c: Some(3),
1872        f: "foo".to_string(),
1873        b: HashMap::default(),
1874      }
1875    );
1876  }
1877
1878  #[test]
1879  fn derive_from_tuple_struct() {
1880    #[derive(deno_ops::FromV8, Eq, PartialEq, Clone, Debug)]
1881    pub struct Tuple(u8, String);
1882    #[derive(deno_ops::FromV8, Eq, PartialEq, Clone, Debug)]
1883    pub struct TupleSingle(u8);
1884
1885    let mut runtime = JsRuntime::new(Default::default());
1886    let val = runtime.execute_script("", "([1, 'foo'])").unwrap();
1887    let val2 = runtime.execute_script("", "(1)").unwrap();
1888
1889    deno_core::scope!(scope, runtime);
1890    let val = Local::new(scope, val);
1891
1892    let from = Tuple::from_v8(scope, val).unwrap();
1893    assert_eq!(from, Tuple(1, "foo".to_string()));
1894
1895    let val2 = Local::new(scope, val2);
1896
1897    let from = TupleSingle::from_v8(scope, val2).unwrap();
1898    assert_eq!(from, TupleSingle(1));
1899  }
1900
1901  #[test]
1902  fn derive_to_struct() {
1903    #[derive(deno_ops::ToV8, Eq, PartialEq, Clone, Debug)]
1904    pub struct Struct {
1905      a: u8,
1906      c: Option<u32>,
1907      #[v8(rename = "e")]
1908      f: String,
1909      #[v8(serde)]
1910      b: HashMap<String, u32>,
1911    }
1912
1913    let mut runtime = JsRuntime::new(Default::default());
1914    deno_core::scope!(scope, runtime);
1915
1916    let from = ToV8::to_v8(
1917      Struct {
1918        a: 1,
1919        c: Some(70000),
1920        f: "foo".to_string(),
1921        b: HashMap::from([("bar".to_string(), 1)]),
1922      },
1923      scope,
1924    )
1925    .unwrap();
1926    let obj = from.cast::<v8::Object>();
1927    let key = v8::String::new(scope, "a").unwrap();
1928    assert_eq!(
1929      obj
1930        .get(scope, key.into())
1931        .unwrap()
1932        .number_value(scope)
1933        .unwrap(),
1934      1.0
1935    );
1936    let key = v8::String::new(scope, "c").unwrap();
1937    assert_eq!(
1938      obj
1939        .get(scope, key.into())
1940        .unwrap()
1941        .number_value(scope)
1942        .unwrap(),
1943      70000.0
1944    );
1945    let key = v8::String::new(scope, "e").unwrap();
1946    assert_eq!(
1947      obj
1948        .get(scope, key.into())
1949        .unwrap()
1950        .to_rust_string_lossy(scope),
1951      "foo"
1952    );
1953    let key = v8::String::new(scope, "b").unwrap();
1954    let record_key = v8::String::new(scope, "bar").unwrap();
1955    assert_eq!(
1956      obj
1957        .get(scope, key.into())
1958        .unwrap()
1959        .cast::<v8::Object>()
1960        .get(scope, record_key.into())
1961        .unwrap()
1962        .number_value(scope)
1963        .unwrap(),
1964      1.0
1965    );
1966
1967    let from = ToV8::to_v8(
1968      Struct {
1969        a: 1,
1970        c: Some(3),
1971        f: "foo".to_string(),
1972        b: HashMap::default(),
1973      },
1974      scope,
1975    )
1976    .unwrap();
1977    let obj = from.cast::<v8::Object>();
1978    let key = v8::String::new(scope, "a").unwrap();
1979    assert_eq!(
1980      obj
1981        .get(scope, key.into())
1982        .unwrap()
1983        .number_value(scope)
1984        .unwrap(),
1985      1.0
1986    );
1987    let key = v8::String::new(scope, "c").unwrap();
1988    assert_eq!(
1989      obj
1990        .get(scope, key.into())
1991        .unwrap()
1992        .number_value(scope)
1993        .unwrap(),
1994      3.0
1995    );
1996    let key = v8::String::new(scope, "e").unwrap();
1997    assert_eq!(
1998      obj
1999        .get(scope, key.into())
2000        .unwrap()
2001        .to_rust_string_lossy(scope),
2002      "foo"
2003    );
2004    let key = v8::String::new(scope, "b").unwrap();
2005    assert!(
2006      obj
2007        .get(scope, key.into())
2008        .unwrap()
2009        .try_cast::<v8::Object>()
2010        .is_ok()
2011    );
2012  }
2013
2014  #[test]
2015  fn derive_to_tuple_struct() {
2016    #[derive(deno_ops::ToV8, Eq, PartialEq, Clone, Debug)]
2017    pub struct Tuple(u8, String);
2018    #[derive(deno_ops::ToV8, Eq, PartialEq, Clone, Debug)]
2019    pub struct TupleSingle(u8);
2020
2021    let mut runtime = JsRuntime::new(Default::default());
2022    deno_core::scope!(scope, runtime);
2023
2024    let from = ToV8::to_v8(Tuple(1, "foo".to_string()), scope).unwrap();
2025    let arr = from.cast::<v8::Array>();
2026    assert_eq!(
2027      arr
2028        .get_index(scope, 0)
2029        .unwrap()
2030        .number_value(scope)
2031        .unwrap(),
2032      1.0
2033    );
2034    assert_eq!(
2035      arr.get_index(scope, 1).unwrap().to_rust_string_lossy(scope),
2036      "foo"
2037    );
2038
2039    let from = ToV8::to_v8(TupleSingle(1), scope).unwrap();
2040    assert_eq!(from.number_value(scope).unwrap(), 1.0);
2041  }
2042
2043  #[test]
2044  fn test_bigint() {
2045    let mut runtime = JsRuntime::new(Default::default());
2046
2047    to_v8_test!(
2048      runtime,
2049      |scope, _args| BigInt {
2050        sign_bit: false,
2051        words: vec![42],
2052      }
2053      .to_v8(scope)
2054      .unwrap(),
2055      "test_fn() === 42n"
2056    );
2057
2058    to_v8_test!(
2059      runtime,
2060      |scope, _args| BigInt {
2061        sign_bit: true,
2062        words: vec![42],
2063      }
2064      .to_v8(scope)
2065      .unwrap(),
2066      "test_fn() === -42n"
2067    );
2068
2069    to_v8_test!(
2070      runtime,
2071      |scope, _args| BigInt {
2072        sign_bit: false,
2073        words: vec![0],
2074      }
2075      .to_v8(scope)
2076      .unwrap(),
2077      "test_fn() === 0n"
2078    );
2079
2080    to_v8_test!(
2081      runtime,
2082      |scope, _args| BigInt {
2083        sign_bit: false,
2084        words: vec![0, 1],
2085      }
2086      .to_v8(scope)
2087      .unwrap(),
2088      "test_fn() === 18446744073709551616n"
2089    );
2090
2091    from_v8_test!(runtime, "42n", |scope, result| {
2092      let bigint = BigInt::from_v8(scope, result).unwrap();
2093      assert!(!bigint.sign_bit);
2094      assert_eq!(bigint.words, vec![42]);
2095    });
2096
2097    from_v8_test!(runtime, "-42n", |scope, result| {
2098      let bigint = BigInt::from_v8(scope, result).unwrap();
2099      assert!(bigint.sign_bit);
2100      assert_eq!(bigint.words, vec![42]);
2101    });
2102
2103    from_v8_test!(runtime, "0n", |scope, result| {
2104      let bigint = BigInt::from_v8(scope, result).unwrap();
2105      assert!(!bigint.sign_bit);
2106      assert!(bigint.words.is_empty());
2107    });
2108
2109    from_v8_test!(runtime, "18446744073709551616n", |scope, result| {
2110      let bigint = BigInt::from_v8(scope, result).unwrap();
2111      assert!(!bigint.sign_bit);
2112      assert_eq!(bigint.words, vec![0, 1]);
2113    });
2114
2115    from_v8_test!(
2116      runtime,
2117      "123456789012345678901234567890n",
2118      |scope, result| {
2119        let bigint = BigInt::from_v8(scope, result).unwrap();
2120        let to_v8 = bigint.to_v8(scope).unwrap();
2121        let back = BigInt::from_v8(scope, to_v8).unwrap();
2122
2123        assert!(!back.sign_bit);
2124        assert!(!back.words.is_empty());
2125      }
2126    );
2127  }
2128}