Created
February 18, 2021 19:42
-
-
Save weskerfoot/76cdedc44fc1f466bcc0e60a8f203a2b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Maybe<bool> Message::Serialize(Environment* env, | |
Local<Context> context, | |
Local<Value> input, | |
const TransferList& transfer_list_v, | |
Local<Object> source_port) { | |
HandleScope handle_scope(env->isolate()); | |
Context::Scope context_scope(context); | |
// Verify that we're not silently overwriting an existing message. | |
CHECK(main_message_buf_.is_empty()); | |
SerializerDelegate delegate(env, context, this); | |
ValueSerializer serializer(env->isolate(), &delegate); | |
delegate.serializer = &serializer; | |
std::vector<Local<ArrayBuffer>> array_buffers; | |
for (uint32_t i = 0; i < transfer_list_v.length(); ++i) { | |
Local<Value> entry = transfer_list_v[i]; | |
// Currently, we support ArrayBuffers and MessagePorts. | |
if (entry->IsArrayBuffer()) { | |
Local<ArrayBuffer> ab = entry.As<ArrayBuffer>(); | |
// If we cannot render the ArrayBuffer unusable in this Isolate and | |
// take ownership of its memory, copying the buffer will have to do. | |
if (!ab->IsDetachable() || ab->IsExternal() || | |
!env->isolate_data()->uses_node_allocator()) { | |
continue; | |
} | |
// See https://github.com/nodejs/node/pull/30339#issuecomment-552225353 | |
// for details. | |
bool untransferrable; | |
if (!ab->HasPrivate( | |
context, | |
env->arraybuffer_untransferable_private_symbol()) | |
.To(&untransferrable)) { | |
return Nothing<bool>(); | |
} | |
if (untransferrable) continue; | |
if (std::find(array_buffers.begin(), array_buffers.end(), ab) != | |
array_buffers.end()) { | |
ThrowDataCloneException( | |
context, | |
FIXED_ONE_BYTE_STRING( | |
env->isolate(), | |
"Transfer list contains duplicate ArrayBuffer")); | |
return Nothing<bool>(); | |
} | |
// We simply use the array index in the `array_buffers` list as the | |
// ID that we write into the serialized buffer. | |
uint32_t id = array_buffers.size(); | |
array_buffers.push_back(ab); | |
serializer.TransferArrayBuffer(id, ab); | |
continue; | |
} else if (env->message_port_constructor_template() | |
->HasInstance(entry)) { | |
// Check if the source MessagePort is being transferred. | |
if (!source_port.IsEmpty() && entry == source_port) { | |
ThrowDataCloneException( | |
context, | |
FIXED_ONE_BYTE_STRING(env->isolate(), | |
"Transfer list contains source port")); | |
return Nothing<bool>(); | |
} | |
MessagePort* port = Unwrap<MessagePort>(entry.As<Object>()); | |
if (port == nullptr || port->IsDetached()) { | |
ThrowDataCloneException( | |
context, | |
FIXED_ONE_BYTE_STRING( | |
env->isolate(), | |
"MessagePort in transfer list is already detached")); | |
return Nothing<bool>(); | |
} | |
if (std::find(delegate.ports_.begin(), delegate.ports_.end(), port) != | |
delegate.ports_.end()) { | |
ThrowDataCloneException( | |
context, | |
FIXED_ONE_BYTE_STRING( | |
env->isolate(), | |
"Transfer list contains duplicate MessagePort")); | |
return Nothing<bool>(); | |
} | |
delegate.ports_.push_back(port); | |
continue; | |
} | |
THROW_ERR_INVALID_TRANSFER_OBJECT(env); | |
return Nothing<bool>(); | |
} | |
serializer.WriteHeader(); | |
if (serializer.WriteValue(context, input).IsNothing()) { | |
return Nothing<bool>(); | |
} | |
for (Local<ArrayBuffer> ab : array_buffers) { | |
// If serialization succeeded, we want to take ownership of | |
// (a.k.a. externalize) the underlying memory region and render | |
// it inaccessible in this Isolate. | |
ArrayBuffer::Contents contents = ab->Externalize(); | |
ab->Detach(); | |
CHECK(env->isolate_data()->uses_node_allocator()); | |
env->isolate_data()->node_allocator()->UnregisterPointer( | |
contents.Data(), contents.ByteLength()); | |
array_buffer_contents_.emplace_back(MallocedBuffer<char>{ | |
static_cast<char*>(contents.Data()), contents.ByteLength()}); | |
} | |
delegate.Finish(); | |
// The serializer gave us a buffer allocated using `malloc()`. | |
std::pair<uint8_t*, size_t> data = serializer.Release(); | |
CHECK_NOT_NULL(data.first); | |
main_message_buf_ = | |
MallocedBuffer<char>(reinterpret_cast<char*>(data.first), data.second); | |
return Just(true); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment