1
0
Fork 0
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:
Luca Casonato 2021-06-26 11:17:05 +02:00 committed by GitHub
parent 1f4cdc067a
commit 22e7b0f585
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 158 additions and 13 deletions

View file

@ -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();
});

View file

@ -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,

View file

@ -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");
}