mirror of
https://github.com/denoland/deno.git
synced 2025-01-08 15:19:40 -05:00
fix: MessagePort in message for postMessage transfers (#11103)
This commit is contained in:
parent
1f4cdc067a
commit
22e7b0f585
3 changed files with 158 additions and 13 deletions
|
@ -31,3 +31,29 @@ Deno.test("messagechannel", async () => {
|
|||
mc.port2.close();
|
||||
mc2.port2.close();
|
||||
});
|
||||
|
||||
Deno.test("messagechannel clone port", async () => {
|
||||
const mc = new MessageChannel();
|
||||
const mc2 = new MessageChannel();
|
||||
assert(mc.port1);
|
||||
assert(mc.port2);
|
||||
|
||||
const promise = deferred();
|
||||
|
||||
mc.port2.onmessage = (e) => {
|
||||
const { port } = e.data;
|
||||
assertEquals(e.ports.length, 1);
|
||||
assert(e.ports[0] instanceof MessagePort);
|
||||
assertEquals(e.ports[0], port);
|
||||
e.ports[0].close();
|
||||
promise.resolve();
|
||||
};
|
||||
|
||||
mc.port1.postMessage({ port: mc2.port1 }, [mc2.port1]);
|
||||
mc.port1.close();
|
||||
|
||||
await promise;
|
||||
|
||||
mc.port2.close();
|
||||
mc2.port2.close();
|
||||
});
|
||||
|
|
135
core/bindings.rs
135
core/bindings.rs
|
@ -11,6 +11,7 @@ use crate::PromiseId;
|
|||
use crate::ZeroCopyBuf;
|
||||
use log::debug;
|
||||
use rusty_v8 as v8;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_v8::to_v8;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -34,6 +35,9 @@ lazy_static::lazy_static! {
|
|||
v8::ExternalReference {
|
||||
function: queue_microtask.map_fn_to()
|
||||
},
|
||||
v8::ExternalReference {
|
||||
function: create_host_object.map_fn_to()
|
||||
},
|
||||
v8::ExternalReference {
|
||||
function: encode.map_fn_to()
|
||||
},
|
||||
|
@ -130,6 +134,7 @@ pub fn initialize_context<'s>(
|
|||
set_func(scope, core_val, "getPromiseDetails", get_promise_details);
|
||||
set_func(scope, core_val, "getProxyDetails", get_proxy_details);
|
||||
set_func(scope, core_val, "memoryUsage", memory_usage);
|
||||
set_func(scope, core_val, "createHostObject", create_host_object);
|
||||
|
||||
// Direct bindings on `window`.
|
||||
set_func(scope, global, "queueMicrotask", queue_microtask);
|
||||
|
@ -514,9 +519,11 @@ fn decode(
|
|||
};
|
||||
}
|
||||
|
||||
struct SerializeDeserialize {}
|
||||
struct SerializeDeserialize<'a> {
|
||||
host_objects: Option<v8::Local<'a, v8::Array>>,
|
||||
}
|
||||
|
||||
impl v8::ValueSerializerImpl for SerializeDeserialize {
|
||||
impl<'a> v8::ValueSerializerImpl for SerializeDeserialize<'a> {
|
||||
#[allow(unused_variables)]
|
||||
fn throw_data_clone_error<'s>(
|
||||
&mut self,
|
||||
|
@ -526,30 +533,104 @@ impl v8::ValueSerializerImpl for SerializeDeserialize {
|
|||
let error = v8::Exception::error(scope, message);
|
||||
scope.throw_exception(error);
|
||||
}
|
||||
|
||||
fn write_host_object<'s>(
|
||||
&mut self,
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
value_serializer: &mut dyn v8::ValueSerializerHelper,
|
||||
) -> Option<bool> {
|
||||
if let Some(host_objects) = self.host_objects {
|
||||
for i in 0..host_objects.length() {
|
||||
let value = host_objects.get_index(scope, i).unwrap();
|
||||
if value == object {
|
||||
value_serializer.write_uint32(i);
|
||||
return Some(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
let message = v8::String::new(scope, "Unsupported object type").unwrap();
|
||||
self.throw_data_clone_error(scope, message);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl v8::ValueDeserializerImpl for SerializeDeserialize {}
|
||||
impl<'a> v8::ValueDeserializerImpl for SerializeDeserialize<'a> {
|
||||
fn read_host_object<'s>(
|
||||
&mut self,
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
value_deserializer: &mut dyn v8::ValueDeserializerHelper,
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
if let Some(host_objects) = self.host_objects {
|
||||
let mut i = 0;
|
||||
if !value_deserializer.read_uint32(&mut i) {
|
||||
return None;
|
||||
}
|
||||
let maybe_value = host_objects.get_index(scope, i);
|
||||
if let Some(value) = maybe_value {
|
||||
return value.to_object(scope);
|
||||
}
|
||||
}
|
||||
|
||||
let message =
|
||||
v8::String::new(scope, "Failed to deserialize host object").unwrap();
|
||||
let error = v8::Exception::error(scope, message);
|
||||
scope.throw_exception(error);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(
|
||||
scope: &mut v8::HandleScope,
|
||||
args: v8::FunctionCallbackArguments,
|
||||
mut rv: v8::ReturnValue,
|
||||
) {
|
||||
let serialize_deserialize = Box::new(SerializeDeserialize {});
|
||||
let value = args.get(0);
|
||||
|
||||
let options: Option<SerializeDeserializeOptions> =
|
||||
match serde_v8::from_v8(scope, args.get(1)) {
|
||||
Ok(opts) => opts,
|
||||
Err(_) => {
|
||||
throw_type_error(scope, "Invalid argument 2");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let options =
|
||||
options.unwrap_or(SerializeDeserializeOptions { host_objects: None });
|
||||
|
||||
let host_objects = match options.host_objects {
|
||||
Some(value) => match v8::Local::<v8::Array>::try_from(value.v8_value) {
|
||||
Ok(host_objects) => Some(host_objects),
|
||||
Err(_) => {
|
||||
throw_type_error(scope, "host_objects not an array");
|
||||
return;
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
let serialize_deserialize = Box::new(SerializeDeserialize { host_objects });
|
||||
let mut value_serializer =
|
||||
v8::ValueSerializer::new(scope, serialize_deserialize);
|
||||
match value_serializer.write_value(scope.get_current_context(), args.get(0)) {
|
||||
match value_serializer.write_value(scope.get_current_context(), value) {
|
||||
Some(true) => {
|
||||
let vector = value_serializer.release();
|
||||
let zbuf: ZeroCopyBuf = vector.into();
|
||||
rv.set(to_v8(scope, zbuf).unwrap());
|
||||
}
|
||||
_ => {
|
||||
throw_type_error(scope, "Invalid argument");
|
||||
throw_type_error(scope, "Failed to serialize response");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct SerializeDeserializeOptions<'a> {
|
||||
host_objects: Option<serde_v8::Value<'a>>,
|
||||
}
|
||||
|
||||
fn deserialize(
|
||||
scope: &mut v8::HandleScope,
|
||||
args: v8::FunctionCallbackArguments,
|
||||
|
@ -558,15 +639,37 @@ fn deserialize(
|
|||
let zero_copy: ZeroCopyBuf = match serde_v8::from_v8(scope, args.get(0)) {
|
||||
Ok(zbuf) => zbuf,
|
||||
Err(_) => {
|
||||
throw_type_error(scope, "Invalid argument");
|
||||
throw_type_error(scope, "Invalid argument 1");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let buf = &zero_copy;
|
||||
|
||||
let serialize_deserialize = Box::new(SerializeDeserialize {});
|
||||
let options: Option<SerializeDeserializeOptions> =
|
||||
match serde_v8::from_v8(scope, args.get(1)) {
|
||||
Ok(opts) => opts,
|
||||
Err(_) => {
|
||||
throw_type_error(scope, "Invalid argument 2");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let options =
|
||||
options.unwrap_or(SerializeDeserializeOptions { host_objects: None });
|
||||
|
||||
let host_objects = match options.host_objects {
|
||||
Some(value) => match v8::Local::<v8::Array>::try_from(value.v8_value) {
|
||||
Ok(host_objects) => Some(host_objects),
|
||||
Err(_) => {
|
||||
throw_type_error(scope, "host_objects not an array");
|
||||
return;
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
let serialize_deserialize = Box::new(SerializeDeserialize { host_objects });
|
||||
let mut value_deserializer =
|
||||
v8::ValueDeserializer::new(scope, serialize_deserialize, buf);
|
||||
v8::ValueDeserializer::new(scope, serialize_deserialize, &zero_copy);
|
||||
let value = value_deserializer.read_value(scope.get_current_context());
|
||||
|
||||
match value {
|
||||
|
@ -592,6 +695,18 @@ fn queue_microtask(
|
|||
};
|
||||
}
|
||||
|
||||
fn create_host_object(
|
||||
scope: &mut v8::HandleScope,
|
||||
_args: v8::FunctionCallbackArguments,
|
||||
mut rv: v8::ReturnValue,
|
||||
) {
|
||||
let template = v8::ObjectTemplate::new(scope);
|
||||
template.set_internal_field_count(1);
|
||||
if let Some(obj) = template.new_instance(scope) {
|
||||
rv.set(obj.into());
|
||||
};
|
||||
}
|
||||
|
||||
/// Called by V8 during `JsRuntime::instantiate_module`.
|
||||
///
|
||||
/// This function borrows `ModuleMap` from the isolate slot,
|
||||
|
|
|
@ -60,7 +60,9 @@
|
|||
* @returns {MessagePort}
|
||||
*/
|
||||
function createMessagePort(id) {
|
||||
const port = webidl.createBranded(MessagePort);
|
||||
const port = core.createHostObject();
|
||||
Object.setPrototypeOf(port, MessagePort.prototype);
|
||||
port[webidl.brand] = webidl.brand;
|
||||
setEventTargetData(port);
|
||||
port[_id] = id;
|
||||
return port;
|
||||
|
@ -187,7 +189,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
const data = core.deserialize(messageData.data);
|
||||
const data = core.deserialize(messageData.data, {
|
||||
hostObjects: transferables,
|
||||
});
|
||||
|
||||
return [data, transferables];
|
||||
}
|
||||
|
@ -200,7 +204,7 @@
|
|||
function serializeJsMessageData(data, tranferables) {
|
||||
let serializedData;
|
||||
try {
|
||||
serializedData = core.serialize(data);
|
||||
serializedData = core.serialize(data, { hostObjects: tranferables });
|
||||
} catch (err) {
|
||||
throw new DOMException(err.message, "DataCloneError");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue