Skip to content

Commit

Permalink
Added Structured Clone
Browse files Browse the repository at this point in the history
  • Loading branch information
Redfire75369 committed Aug 5, 2024
1 parent 2a755df commit 6f797a2
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 0 deletions.
133 changes: 133 additions & 0 deletions runtime/src/globals/clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

use std::ptr;

use mozjs::glue::{
CopyJSStructuredCloneData, DeleteJSAutoStructuredCloneBuffer, GetLengthOfJSStructuredCloneData,
NewJSAutoStructuredCloneBuffer, WriteBytesToJSStructuredCloneData,
};
use mozjs::jsapi::{
CloneDataPolicy, JS_ReadStructuredClone, JS_STRUCTURED_CLONE_VERSION, JS_WriteStructuredClone,
JSStructuredCloneCallbacks, StructuredCloneScope,
};

use ion::{Context, Exception, Object, ResultExc, Value};
use ion::conversions::ToValue;
use ion::flags::PropertyFlags;
use ion::function::Opt;

static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredCloneCallbacks {
read: None,
write: None,
reportError: None,
readTransfer: None,
writeTransfer: None,
freeTransfer: None,
canTransfer: None,
sabCloned: None,
};

pub fn write(
cx: &Context, data: Value, transfer: Option<Vec<Object>>, scope: StructuredCloneScope,
) -> ResultExc<Vec<u8>> {
let transfer = transfer.map_or_else(|| Value::undefined(cx), |t| t.as_value(cx));

unsafe {
let buffer = NewJSAutoStructuredCloneBuffer(scope, &STRUCTURED_CLONE_CALLBACKS);
let scdata = &mut (*buffer).data_;

let policy = CloneDataPolicy {
allowIntraClusterClonableSharedObjects_: false,
allowSharedMemoryObjects_: true,
};

let res = JS_WriteStructuredClone(
cx.as_ptr(),
data.handle().into(),
scdata,
scope,
&policy,
&STRUCTURED_CLONE_CALLBACKS,
ptr::null_mut(),
transfer.handle().into(),
);

if !res {
return Err(Exception::new(cx)?.unwrap());
}

let len = GetLengthOfJSStructuredCloneData(scdata);
let mut data = Vec::with_capacity(len);
CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
data.set_len(len);

DeleteJSAutoStructuredCloneBuffer(buffer);

Ok(data)
}
}

pub fn read(cx: &Context, data: Vec<u8>, scope: StructuredCloneScope) -> ResultExc<Value> {
let mut rval = Value::undefined(cx);
unsafe {
let buffer = NewJSAutoStructuredCloneBuffer(scope, &STRUCTURED_CLONE_CALLBACKS);
let scdata = &mut (*buffer).data_;

WriteBytesToJSStructuredCloneData(data.as_ptr(), data.len(), scdata);

let policy = CloneDataPolicy {
allowIntraClusterClonableSharedObjects_: false,
allowSharedMemoryObjects_: true,
};

let res = JS_ReadStructuredClone(
cx.as_ptr(),
scdata,
JS_STRUCTURED_CLONE_VERSION,
scope,
rval.handle_mut().into(),
&policy,
&STRUCTURED_CLONE_CALLBACKS,
ptr::null_mut(),
);

DeleteJSAutoStructuredCloneBuffer(buffer);

if !res {
Err(Exception::new(cx)?.unwrap())
} else {
Ok(rval)
}
}
}

#[derive(FromValue)]
struct StructuredCloneOptions<'cx> {
transfer: Vec<Object<'cx>>,
}

#[js_fn]
fn structured_clone<'cx>(
cx: &'cx Context, data: Value<'cx>, Opt(options): Opt<StructuredCloneOptions<'cx>>,
) -> ResultExc<Value<'cx>> {
let transfer = options.map(|o| o.transfer);
let data = write(cx, data, transfer, StructuredCloneScope::DifferentProcess)?;
read(cx, data, StructuredCloneScope::DifferentProcess)
}

pub fn define(cx: &Context, global: &Object) -> bool {
!global
.define_method(
cx,
"structuredClone",
structured_clone,
1,
PropertyFlags::CONSTANT_ENUMERATED,
)
.handle()
.is_null()
}
2 changes: 2 additions & 0 deletions runtime/src/globals/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use ion::{ClassDefinition, Context, Iterator, Object};

pub mod abort;
pub mod base64;
pub mod clone;
pub mod console;
pub mod encoding;
#[cfg(feature = "fetch")]
Expand All @@ -19,6 +20,7 @@ pub mod url;

pub fn init_globals(cx: &Context, global: &Object) -> bool {
let result = base64::define(cx, global)
&& clone::define(cx, global)
&& console::define(cx, global)
&& encoding::define(cx, global)
&& file::define(cx, global)
Expand Down

0 comments on commit 6f797a2

Please sign in to comment.