ion/
clone.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use std::any::Any;
8use std::ffi::c_void;
9use std::ptr;
10
11use mozjs::glue::{
12	CopyJSStructuredCloneData, DeleteJSAutoStructuredCloneBuffer, GetLengthOfJSStructuredCloneData,
13	NewJSAutoStructuredCloneBuffer, WriteBytesToJSStructuredCloneData,
14};
15use mozjs::jsapi::{
16	CloneDataPolicy, JS_ReadStructuredClone, JS_ReadUint32Pair, JS_STRUCTURED_CLONE_VERSION, JS_WriteStructuredClone,
17	JS_WriteUint32Pair, JSAutoStructuredCloneBuffer, JSStructuredCloneCallbacks, JSStructuredCloneReader,
18	JSStructuredCloneWriter, StructuredCloneScope,
19};
20
21use crate::conversions::ToValue as _;
22use crate::{Context, Exception, Object, ResultExc, Value};
23
24pub struct StructuredCloneBuffer {
25	buf: *mut JSAutoStructuredCloneBuffer,
26	scope: StructuredCloneScope,
27	callbacks: &'static JSStructuredCloneCallbacks,
28	data: Box<Option<Box<dyn Any>>>,
29}
30
31impl StructuredCloneBuffer {
32	pub fn new(
33		scope: StructuredCloneScope, callbacks: &'static JSStructuredCloneCallbacks, data: Option<Box<dyn Any>>,
34	) -> StructuredCloneBuffer {
35		StructuredCloneBuffer {
36			buf: unsafe { NewJSAutoStructuredCloneBuffer(scope, callbacks) },
37			scope,
38			callbacks,
39			data: Box::new(data),
40		}
41	}
42
43	pub fn write(
44		&mut self, cx: &Context, data: &Value, transfer: Option<Vec<Object>>, policy: &CloneDataPolicy,
45	) -> ResultExc<()> {
46		let transfer = transfer.map_or_else(|| Value::undefined(cx), |t| t.as_value(cx));
47
48		unsafe {
49			let scdata = &mut (*self.buf).data_;
50
51			let res = JS_WriteStructuredClone(
52				cx.as_ptr(),
53				data.handle().into(),
54				scdata,
55				self.scope,
56				policy,
57				self.callbacks,
58				self.data_ptr(),
59				transfer.handle().into(),
60			);
61
62			if res { Ok(()) } else { Err(Exception::new(cx)?.unwrap()) }
63		}
64	}
65
66	pub fn read<'cx>(&self, cx: &'cx Context, policy: &CloneDataPolicy) -> ResultExc<Value<'cx>> {
67		let mut rval = Value::undefined(cx);
68		unsafe {
69			let scdata = &mut (*self.buf).data_;
70
71			let res = JS_ReadStructuredClone(
72				cx.as_ptr(),
73				scdata,
74				JS_STRUCTURED_CLONE_VERSION,
75				self.scope,
76				rval.handle_mut().into(),
77				policy,
78				self.callbacks,
79				self.data_ptr(),
80			);
81
82			if res {
83				Ok(rval)
84			} else {
85				Err(Exception::new(cx)?.unwrap())
86			}
87		}
88	}
89
90	/// Converts the buffer into bytes. If the buffer contains pointers
91	/// (contains transferable objects and scope is JSStructuredCloneScope::SameProcess),
92	/// the Buffer must remain alive for the transferable objects to be valid.
93	pub unsafe fn to_vec(&self) -> Vec<u8> {
94		unsafe {
95			let scdata = &mut (*self.buf).data_;
96
97			let len = GetLengthOfJSStructuredCloneData(scdata);
98			let mut data = Vec::with_capacity(len);
99			CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
100			data.set_len(len);
101
102			data
103		}
104	}
105
106	/// Reads the data into the buffer.
107	/// The data must not contain pointers (see [StructuredCloneBuffer::to_vec]).
108	pub unsafe fn write_from_bytes(&self, data: &[u8]) {
109		unsafe {
110			let scdata = &mut (*self.buf).data_;
111			WriteBytesToJSStructuredCloneData(data.as_ptr(), data.len(), scdata);
112		}
113	}
114
115	fn data_ptr(&self) -> *mut c_void {
116		ptr::from_ref(&*self.data).cast::<c_void>().cast_mut()
117	}
118}
119
120impl Drop for StructuredCloneBuffer {
121	fn drop(&mut self) {
122		unsafe {
123			DeleteJSAutoStructuredCloneBuffer(self.buf);
124		}
125	}
126}
127
128pub unsafe fn read_uint64(r: *mut JSStructuredCloneReader) -> Option<u64> {
129	let mut high = 0;
130	let mut low = 0;
131	let res = unsafe { JS_ReadUint32Pair(r, &raw mut high, &raw mut low) };
132	res.then_some((u64::from(high) << 32) | u64::from(low))
133}
134
135pub unsafe fn write_uint64(w: *mut JSStructuredCloneWriter, data: u64) -> bool {
136	unsafe { JS_WriteUint32Pair(w, (data >> 32) as u32, (data & 0xFFFFFFFF) as u32) }
137}