mirror of
https://github.com/denoland/deno.git
synced 2025-01-09 15:48:16 -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();
|
mc.port2.close();
|
||||||
mc2.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 crate::ZeroCopyBuf;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rusty_v8 as v8;
|
use rusty_v8 as v8;
|
||||||
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_v8::to_v8;
|
use serde_v8::to_v8;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -34,6 +35,9 @@ lazy_static::lazy_static! {
|
||||||
v8::ExternalReference {
|
v8::ExternalReference {
|
||||||
function: queue_microtask.map_fn_to()
|
function: queue_microtask.map_fn_to()
|
||||||
},
|
},
|
||||||
|
v8::ExternalReference {
|
||||||
|
function: create_host_object.map_fn_to()
|
||||||
|
},
|
||||||
v8::ExternalReference {
|
v8::ExternalReference {
|
||||||
function: encode.map_fn_to()
|
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, "getPromiseDetails", get_promise_details);
|
||||||
set_func(scope, core_val, "getProxyDetails", get_proxy_details);
|
set_func(scope, core_val, "getProxyDetails", get_proxy_details);
|
||||||
set_func(scope, core_val, "memoryUsage", memory_usage);
|
set_func(scope, core_val, "memoryUsage", memory_usage);
|
||||||
|
set_func(scope, core_val, "createHostObject", create_host_object);
|
||||||
|
|
||||||
// Direct bindings on `window`.
|
// Direct bindings on `window`.
|
||||||
set_func(scope, global, "queueMicrotask", queue_microtask);
|
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)]
|
#[allow(unused_variables)]
|
||||||
fn throw_data_clone_error<'s>(
|
fn throw_data_clone_error<'s>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -526,30 +533,104 @@ impl v8::ValueSerializerImpl for SerializeDeserialize {
|
||||||
let error = v8::Exception::error(scope, message);
|
let error = v8::Exception::error(scope, message);
|
||||||
scope.throw_exception(error);
|
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(
|
fn serialize(
|
||||||
scope: &mut v8::HandleScope,
|
scope: &mut v8::HandleScope,
|
||||||
args: v8::FunctionCallbackArguments,
|
args: v8::FunctionCallbackArguments,
|
||||||
mut rv: v8::ReturnValue,
|
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 =
|
let mut value_serializer =
|
||||||
v8::ValueSerializer::new(scope, serialize_deserialize);
|
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) => {
|
Some(true) => {
|
||||||
let vector = value_serializer.release();
|
let vector = value_serializer.release();
|
||||||
let zbuf: ZeroCopyBuf = vector.into();
|
let zbuf: ZeroCopyBuf = vector.into();
|
||||||
rv.set(to_v8(scope, zbuf).unwrap());
|
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(
|
fn deserialize(
|
||||||
scope: &mut v8::HandleScope,
|
scope: &mut v8::HandleScope,
|
||||||
args: v8::FunctionCallbackArguments,
|
args: v8::FunctionCallbackArguments,
|
||||||
|
@ -558,15 +639,37 @@ fn deserialize(
|
||||||
let zero_copy: ZeroCopyBuf = match serde_v8::from_v8(scope, args.get(0)) {
|
let zero_copy: ZeroCopyBuf = match serde_v8::from_v8(scope, args.get(0)) {
|
||||||
Ok(zbuf) => zbuf,
|
Ok(zbuf) => zbuf,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
throw_type_error(scope, "Invalid argument");
|
throw_type_error(scope, "Invalid argument 1");
|
||||||
return;
|
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 =
|
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());
|
let value = value_deserializer.read_value(scope.get_current_context());
|
||||||
|
|
||||||
match value {
|
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`.
|
/// Called by V8 during `JsRuntime::instantiate_module`.
|
||||||
///
|
///
|
||||||
/// This function borrows `ModuleMap` from the isolate slot,
|
/// This function borrows `ModuleMap` from the isolate slot,
|
||||||
|
|
|
@ -60,7 +60,9 @@
|
||||||
* @returns {MessagePort}
|
* @returns {MessagePort}
|
||||||
*/
|
*/
|
||||||
function createMessagePort(id) {
|
function createMessagePort(id) {
|
||||||
const port = webidl.createBranded(MessagePort);
|
const port = core.createHostObject();
|
||||||
|
Object.setPrototypeOf(port, MessagePort.prototype);
|
||||||
|
port[webidl.brand] = webidl.brand;
|
||||||
setEventTargetData(port);
|
setEventTargetData(port);
|
||||||
port[_id] = id;
|
port[_id] = id;
|
||||||
return port;
|
return port;
|
||||||
|
@ -187,7 +189,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = core.deserialize(messageData.data);
|
const data = core.deserialize(messageData.data, {
|
||||||
|
hostObjects: transferables,
|
||||||
|
});
|
||||||
|
|
||||||
return [data, transferables];
|
return [data, transferables];
|
||||||
}
|
}
|
||||||
|
@ -200,7 +204,7 @@
|
||||||
function serializeJsMessageData(data, tranferables) {
|
function serializeJsMessageData(data, tranferables) {
|
||||||
let serializedData;
|
let serializedData;
|
||||||
try {
|
try {
|
||||||
serializedData = core.serialize(data);
|
serializedData = core.serialize(data, { hostObjects: tranferables });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new DOMException(err.message, "DataCloneError");
|
throw new DOMException(err.message, "DataCloneError");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue