use boa_engine::bigint::RawBigInt;
use boa_engine::builtins::array_buffer::{AlignedVec, SharedArrayBuffer};
use boa_engine::builtins::error::ErrorKind;
use boa_engine::builtins::typed_array::TypedArrayKind;
use boa_engine::value::TryIntoJs;
use boa_engine::{Context, JsError, JsResult, JsString, JsValue, JsVariant, js_error};
use std::collections::HashSet;
use std::sync::Arc;
mod from;
mod to;
#[inline]
fn unsupported_type() -> JsError {
js_error!(Error: "DataCloneError: unsupported type for structured data")
}
#[inline]
fn unsupported_transfer() -> JsError {
js_error!(TypeError: "Found an invalid value in transferList")
}
#[derive(Debug, Eq, PartialEq, Hash)]
struct StringStore(Vec<u16>);
impl StringStore {
fn to_js_string(&self) -> JsString {
JsString::from(self.0.as_slice())
}
}
impl From<JsString> for StringStore {
fn from(value: JsString) -> Self {
Self(value.to_vec())
}
}
impl From<StringStore> for JsString {
fn from(value: StringStore) -> Self {
JsString::from(value.0.as_slice())
}
}
#[derive(Debug)]
enum ValueStoreInner {
Empty,
Null,
Undefined,
Boolean(bool),
Float(f64),
String(StringStore),
BigInt(RawBigInt),
Object(Vec<(StringStore, JsValueStore)>),
Map(Vec<(JsValueStore, JsValueStore)>),
Set(Vec<JsValueStore>),
Array(Vec<Option<JsValueStore>>),
Date(f64),
#[expect(unused)]
Error {
kind: ErrorKind,
name: StringStore,
message: StringStore,
stack: StringStore,
cause: StringStore,
},
RegExp { source: String, flags: String },
ArrayBuffer(AlignedVec<u8>),
SharedArrayBuffer(SharedArrayBuffer),
#[expect(unused)]
DataView {
buffer: JsValueStore,
byte_length: u64,
byte_offset: u64,
},
TypedArray {
kind: TypedArrayKind,
buffer: JsValueStore,
},
}
#[derive(Debug, Clone)]
pub struct JsValueStore(Arc<ValueStoreInner>);
impl TryIntoJs for JsValueStore {
fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {
let mut seen = to::ReverseSeenMap::default();
to::try_value_into_js(self, &mut seen, context)
}
}
impl JsValueStore {
unsafe fn replace(&mut self, other: ValueStoreInner) {
let ptr = Arc::as_ptr(&self.0).cast_mut();
assert!(!ptr.is_null());
unsafe {
assert!(
matches!(*ptr, ValueStoreInner::Empty),
"ValueStoreInner must be empty."
);
*ptr = other;
}
}
fn empty() -> Self {
Self(Arc::new(ValueStoreInner::Empty))
}
fn new(inner: ValueStoreInner) -> Self {
Self(Arc::new(inner))
}
pub fn try_from_js(
value: &JsValue,
context: &mut Context,
transfer: Vec<JsValue>,
) -> JsResult<Self> {
let mut seen = from::SeenMap::default();
let transfer = transfer
.into_iter()
.map(|v| match v.variant() {
JsVariant::Object(o) if from::is_transferable(&o) => Ok(o),
_ => Err(unsupported_transfer()),
})
.collect::<Result<HashSet<_>, _>>()?;
let v = from::try_from_js_value(value, &transfer, &mut seen, context)?;
Ok(v)
}
}