#pragma once #include "external_copy.h" #include "isolate/environment.h" #include #include #include namespace ivm { namespace detail { class SerializationDelegateBase { public: SerializationDelegateBase( std::deque>& transferables, std::deque& wasm_modules ) : transferables{transferables}, wasm_modules{wasm_modules} {} protected: std::deque>& transferables; std::deque& wasm_modules; }; class SerializerDelegate : public SerializationDelegateBase, public v8::ValueSerializer::Delegate { public: using SerializationDelegateBase::SerializationDelegateBase; void SetSerializer(v8::ValueSerializer* serializer) { this->serializer = serializer; } void ThrowDataCloneError(v8::Local message) final; auto GetSharedArrayBufferId( v8::Isolate* isolate, v8::Local shared_array_buffer) -> v8::Maybe final; auto GetWasmModuleTransferId( v8::Isolate* isolate, v8::Local module) -> v8::Maybe final; auto WriteHostObject(v8::Isolate* isolate, v8::Local object) -> v8::Maybe final; private: v8::ValueSerializer* serializer = nullptr; }; class DeserializerDelegate : public SerializationDelegateBase, public v8::ValueDeserializer::Delegate { public: using SerializationDelegateBase::SerializationDelegateBase; void SetDeserializer(v8::ValueDeserializer* deserializer) { this->deserializer = deserializer; } auto GetSharedArrayBufferFromId( v8::Isolate* isolate, uint32_t clone_id) -> v8::MaybeLocal final; auto GetWasmModuleFromId( v8::Isolate* isolate, uint32_t transfer_id) -> v8::MaybeLocal final; auto ReadHostObject(v8::Isolate* isolate) -> v8::MaybeLocal final; private: v8::ValueDeserializer* deserializer = nullptr; }; } // namespace detail /** * Abstract serializer which manages boilerplate and lifecycle management of the serialized blob */ class BaseSerializer { protected: template explicit BaseSerializer(Fn fn) { // Initialize serializer and delegate auto* isolate = v8::Isolate::GetCurrent(); auto context = isolate->GetCurrentContext(); detail::SerializerDelegate delegate{transferables, wasm_modules}; v8::ValueSerializer serializer{isolate, &delegate}; delegate.SetSerializer(&serializer); // Run serialization serializer.WriteHeader(); fn(serializer, context); // Save to buffer auto serialized_data = serializer.Release(); buffer = {serialized_data.first, std::free}; size = serialized_data.second; } template auto Deserialize(Fn fn) { // Initialize deserializer and delegate auto* isolate = v8::Isolate::GetCurrent(); auto context = isolate->GetCurrentContext(); detail::DeserializerDelegate delegate{transferables, wasm_modules}; v8::ValueDeserializer deserializer{isolate, buffer.get(), size, &delegate}; delegate.SetDeserializer(&deserializer); // Watch for allocation errors auto* allocator = IsolateEnvironment::GetCurrent().GetLimitedAllocator(); int failures = allocator == nullptr ? 0 : allocator->GetFailureCount(); Unmaybe(deserializer.ReadHeader(context)); // Run implementation if (!fn(deserializer, context)) { // ValueDeserializer throws an unhelpful message when it fails to allocate an ArrayBuffer, so // detect that case here and throw an appropriate message. if (allocator != nullptr && allocator->GetFailureCount() != failures) { throw RuntimeRangeError("Array buffer allocation failed"); } else { throw RuntimeError(); } } } private: std::unique_ptr buffer = {nullptr, std::free}; std::deque> transferables; std::deque wasm_modules; size_t size; }; /** * Single serialized value compatible with `Transferable` */ class ExternalCopySerialized : public BaseSerializer, public ExternalCopy { public: ExternalCopySerialized(v8::Local value, ArrayRange transfer_list); auto CopyInto(bool transfer_in = false) -> v8::Local final; private: std::vector> array_buffers; }; class SerializedVector : public BaseSerializer { public: explicit SerializedVector(const v8::FunctionCallbackInfo& info); auto CopyIntoAsVector() -> std::vector>; private: std::vector> array_buffers; }; } // namespace ivm