#include "node_internals.h" #include "node_buffer.h" #include "node_errors.h" #include "util-inl.h" #include "base_object-inl.h" namespace node { using v8::Array; using v8::ArrayBuffer; using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Integer; using v8::Isolate; using v8::Just; using v8::Local; using v8::Maybe; using v8::MaybeLocal; using v8::Nothing; using v8::Object; using v8::SharedArrayBuffer; using v8::String; using v8::Value; using v8::ValueDeserializer; using v8::ValueSerializer; namespace { class SerializerContext : public BaseObject, public ValueSerializer::Delegate { public: SerializerContext(Environment* env, Local wrap); ~SerializerContext() override = default; void ThrowDataCloneError(Local message) override; Maybe WriteHostObject(Isolate* isolate, Local object) override; Maybe GetSharedArrayBufferId( Isolate* isolate, Local shared_array_buffer) override; static void SetTreatArrayBufferViewsAsHostObjects( const FunctionCallbackInfo& args); static void New(const FunctionCallbackInfo& args); static void WriteHeader(const FunctionCallbackInfo& args); static void WriteValue(const FunctionCallbackInfo& args); static void ReleaseBuffer(const FunctionCallbackInfo& args); static void TransferArrayBuffer(const FunctionCallbackInfo& args); static void WriteUint32(const FunctionCallbackInfo& args); static void WriteUint64(const FunctionCallbackInfo& args); static void WriteDouble(const FunctionCallbackInfo& args); static void WriteRawBytes(const FunctionCallbackInfo& args); SET_NO_MEMORY_INFO() SET_MEMORY_INFO_NAME(SerializerContext) SET_SELF_SIZE(SerializerContext) private: ValueSerializer serializer_; }; class DeserializerContext : public BaseObject, public ValueDeserializer::Delegate { public: DeserializerContext(Environment* env, Local wrap, Local buffer); ~DeserializerContext() override = default; MaybeLocal ReadHostObject(Isolate* isolate) override; static void New(const FunctionCallbackInfo& args); static void ReadHeader(const FunctionCallbackInfo& args); static void ReadValue(const FunctionCallbackInfo& args); static void TransferArrayBuffer(const FunctionCallbackInfo& args); static void GetWireFormatVersion(const FunctionCallbackInfo& args); static void ReadUint32(const FunctionCallbackInfo& args); static void ReadUint64(const FunctionCallbackInfo& args); static void ReadDouble(const FunctionCallbackInfo& args); static void ReadRawBytes(const FunctionCallbackInfo& args); SET_NO_MEMORY_INFO() SET_MEMORY_INFO_NAME(DeserializerContext) SET_SELF_SIZE(DeserializerContext) private: const uint8_t* data_; const size_t length_; ValueDeserializer deserializer_; }; SerializerContext::SerializerContext(Environment* env, Local wrap) : BaseObject(env, wrap), serializer_(env->isolate(), this) { MakeWeak(); } void SerializerContext::ThrowDataCloneError(Local message) { Local args[1] = { message }; Local get_data_clone_error = object()->Get(env()->context(), env()->get_data_clone_error_string()) .ToLocalChecked(); CHECK(get_data_clone_error->IsFunction()); MaybeLocal error = get_data_clone_error.As()->Call(env()->context(), object(), arraysize(args), args); if (error.IsEmpty()) return; env()->isolate()->ThrowException(error.ToLocalChecked()); } Maybe SerializerContext::GetSharedArrayBufferId( Isolate* isolate, Local shared_array_buffer) { Local args[1] = { shared_array_buffer }; Local get_shared_array_buffer_id = object()->Get(env()->context(), env()->get_shared_array_buffer_id_string()) .ToLocalChecked(); if (!get_shared_array_buffer_id->IsFunction()) { return ValueSerializer::Delegate::GetSharedArrayBufferId( isolate, shared_array_buffer); } MaybeLocal id = get_shared_array_buffer_id.As()->Call(env()->context(), object(), arraysize(args), args); if (id.IsEmpty()) return Nothing(); return id.ToLocalChecked()->Uint32Value(env()->context()); } Maybe SerializerContext::WriteHostObject(Isolate* isolate, Local input) { MaybeLocal ret; Local args[1] = { input }; Local write_host_object = object()->Get(env()->context(), env()->write_host_object_string()).ToLocalChecked(); if (!write_host_object->IsFunction()) { return ValueSerializer::Delegate::WriteHostObject(isolate, input); } ret = write_host_object.As()->Call(env()->context(), object(), arraysize(args), args); if (ret.IsEmpty()) return Nothing(); return Just(true); } void SerializerContext::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); new SerializerContext(env, args.This()); } void SerializerContext::WriteHeader(const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); ctx->serializer_.WriteHeader(); } void SerializerContext::WriteValue(const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); Maybe ret = ctx->serializer_.WriteValue(ctx->env()->context(), args[0]); if (ret.IsJust()) args.GetReturnValue().Set(ret.FromJust()); } void SerializerContext::SetTreatArrayBufferViewsAsHostObjects( const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); bool value = args[0]->BooleanValue(ctx->env()->isolate()); ctx->serializer_.SetTreatArrayBufferViewsAsHostObjects(value); } void SerializerContext::ReleaseBuffer(const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); // Note: Both ValueSerializer and this Buffer::New() variant use malloc() // as the underlying allocator. std::pair ret = ctx->serializer_.Release(); auto buf = Buffer::New(ctx->env(), reinterpret_cast(ret.first), ret.second, true /* uses_malloc */); if (!buf.IsEmpty()) { args.GetReturnValue().Set(buf.ToLocalChecked()); } } void SerializerContext::TransferArrayBuffer( const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); Maybe id = args[0]->Uint32Value(ctx->env()->context()); if (id.IsNothing()) return; if (!args[1]->IsArrayBuffer()) return node::THROW_ERR_INVALID_ARG_TYPE( ctx->env(), "arrayBuffer must be an ArrayBuffer"); Local ab = args[1].As(); ctx->serializer_.TransferArrayBuffer(id.FromJust(), ab); return; } void SerializerContext::WriteUint32(const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); Maybe value = args[0]->Uint32Value(ctx->env()->context()); if (value.IsNothing()) return; ctx->serializer_.WriteUint32(value.FromJust()); } void SerializerContext::WriteUint64(const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); Maybe arg0 = args[0]->Uint32Value(ctx->env()->context()); Maybe arg1 = args[1]->Uint32Value(ctx->env()->context()); if (arg0.IsNothing() || arg1.IsNothing()) return; uint64_t hi = arg0.FromJust(); uint64_t lo = arg1.FromJust(); ctx->serializer_.WriteUint64((hi << 32) | lo); } void SerializerContext::WriteDouble(const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); Maybe value = args[0]->NumberValue(ctx->env()->context()); if (value.IsNothing()) return; ctx->serializer_.WriteDouble(value.FromJust()); } void SerializerContext::WriteRawBytes(const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); if (!args[0]->IsArrayBufferView()) { return node::THROW_ERR_INVALID_ARG_TYPE( ctx->env(), "source must be a TypedArray or a DataView"); } ArrayBufferViewContents bytes(args[0]); ctx->serializer_.WriteRawBytes(bytes.data(), bytes.length()); } DeserializerContext::DeserializerContext(Environment* env, Local wrap, Local buffer) : BaseObject(env, wrap), data_(reinterpret_cast(Buffer::Data(buffer))), length_(Buffer::Length(buffer)), deserializer_(env->isolate(), data_, length_, this) { object()->Set(env->context(), env->buffer_string(), buffer).Check(); deserializer_.SetExpectInlineWasm(true); MakeWeak(); } MaybeLocal DeserializerContext::ReadHostObject(Isolate* isolate) { Local read_host_object = object()->Get(env()->context(), env()->read_host_object_string()).ToLocalChecked(); if (!read_host_object->IsFunction()) { return ValueDeserializer::Delegate::ReadHostObject(isolate); } Isolate::AllowJavascriptExecutionScope allow_js(isolate); MaybeLocal ret = read_host_object.As()->Call(env()->context(), object(), 0, nullptr); if (ret.IsEmpty()) return MaybeLocal(); Local return_value = ret.ToLocalChecked(); if (!return_value->IsObject()) { env()->ThrowTypeError("readHostObject must return an object"); return MaybeLocal(); } return return_value.As(); } void DeserializerContext::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsArrayBufferView()) { return node::THROW_ERR_INVALID_ARG_TYPE( env, "buffer must be a TypedArray or a DataView"); } new DeserializerContext(env, args.This(), args[0]); } void DeserializerContext::ReadHeader(const FunctionCallbackInfo& args) { DeserializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); Maybe ret = ctx->deserializer_.ReadHeader(ctx->env()->context()); if (ret.IsJust()) args.GetReturnValue().Set(ret.FromJust()); } void DeserializerContext::ReadValue(const FunctionCallbackInfo& args) { DeserializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); MaybeLocal ret = ctx->deserializer_.ReadValue(ctx->env()->context()); if (!ret.IsEmpty()) args.GetReturnValue().Set(ret.ToLocalChecked()); } void DeserializerContext::TransferArrayBuffer( const FunctionCallbackInfo& args) { DeserializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); Maybe id = args[0]->Uint32Value(ctx->env()->context()); if (id.IsNothing()) return; if (args[1]->IsArrayBuffer()) { Local ab = args[1].As(); ctx->deserializer_.TransferArrayBuffer(id.FromJust(), ab); return; } if (args[1]->IsSharedArrayBuffer()) { Local sab = args[1].As(); ctx->deserializer_.TransferSharedArrayBuffer(id.FromJust(), sab); return; } return node::THROW_ERR_INVALID_ARG_TYPE( ctx->env(), "arrayBuffer must be an ArrayBuffer or SharedArrayBuffer"); } void DeserializerContext::GetWireFormatVersion( const FunctionCallbackInfo& args) { DeserializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); args.GetReturnValue().Set(ctx->deserializer_.GetWireFormatVersion()); } void DeserializerContext::ReadUint32(const FunctionCallbackInfo& args) { DeserializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); uint32_t value; bool ok = ctx->deserializer_.ReadUint32(&value); if (!ok) return ctx->env()->ThrowError("ReadUint32() failed"); return args.GetReturnValue().Set(value); } void DeserializerContext::ReadUint64(const FunctionCallbackInfo& args) { DeserializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); uint64_t value; bool ok = ctx->deserializer_.ReadUint64(&value); if (!ok) return ctx->env()->ThrowError("ReadUint64() failed"); uint32_t hi = static_cast(value >> 32); uint32_t lo = static_cast(value); Isolate* isolate = ctx->env()->isolate(); Local ret[] = { Integer::NewFromUnsigned(isolate, hi), Integer::NewFromUnsigned(isolate, lo) }; return args.GetReturnValue().Set(Array::New(isolate, ret, arraysize(ret))); } void DeserializerContext::ReadDouble(const FunctionCallbackInfo& args) { DeserializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); double value; bool ok = ctx->deserializer_.ReadDouble(&value); if (!ok) return ctx->env()->ThrowError("ReadDouble() failed"); return args.GetReturnValue().Set(value); } void DeserializerContext::ReadRawBytes( const FunctionCallbackInfo& args) { DeserializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); Maybe length_arg = args[0]->IntegerValue(ctx->env()->context()); if (length_arg.IsNothing()) return; size_t length = length_arg.FromJust(); const void* data; bool ok = ctx->deserializer_.ReadRawBytes(length, &data); if (!ok) return ctx->env()->ThrowError("ReadRawBytes() failed"); const uint8_t* position = reinterpret_cast(data); CHECK_GE(position, ctx->data_); CHECK_LE(position + length, ctx->data_ + ctx->length_); const uint32_t offset = position - ctx->data_; CHECK_EQ(ctx->data_ + offset, position); args.GetReturnValue().Set(offset); } void Initialize(Local target, Local unused, Local context, void* priv) { Environment* env = Environment::GetCurrent(context); Local ser = env->NewFunctionTemplate(SerializerContext::New); ser->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(ser, "writeHeader", SerializerContext::WriteHeader); env->SetProtoMethod(ser, "writeValue", SerializerContext::WriteValue); env->SetProtoMethod(ser, "releaseBuffer", SerializerContext::ReleaseBuffer); env->SetProtoMethod(ser, "transferArrayBuffer", SerializerContext::TransferArrayBuffer); env->SetProtoMethod(ser, "writeUint32", SerializerContext::WriteUint32); env->SetProtoMethod(ser, "writeUint64", SerializerContext::WriteUint64); env->SetProtoMethod(ser, "writeDouble", SerializerContext::WriteDouble); env->SetProtoMethod(ser, "writeRawBytes", SerializerContext::WriteRawBytes); env->SetProtoMethod(ser, "_setTreatArrayBufferViewsAsHostObjects", SerializerContext::SetTreatArrayBufferViewsAsHostObjects); Local serializerString = FIXED_ONE_BYTE_STRING(env->isolate(), "Serializer"); ser->SetClassName(serializerString); target->Set(env->context(), serializerString, ser->GetFunction(env->context()).ToLocalChecked()).Check(); Local des = env->NewFunctionTemplate(DeserializerContext::New); des->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(des, "readHeader", DeserializerContext::ReadHeader); env->SetProtoMethod(des, "readValue", DeserializerContext::ReadValue); env->SetProtoMethod(des, "getWireFormatVersion", DeserializerContext::GetWireFormatVersion); env->SetProtoMethod(des, "transferArrayBuffer", DeserializerContext::TransferArrayBuffer); env->SetProtoMethod(des, "readUint32", DeserializerContext::ReadUint32); env->SetProtoMethod(des, "readUint64", DeserializerContext::ReadUint64); env->SetProtoMethod(des, "readDouble", DeserializerContext::ReadDouble); env->SetProtoMethod(des, "_readRawBytes", DeserializerContext::ReadRawBytes); Local deserializerString = FIXED_ONE_BYTE_STRING(env->isolate(), "Deserializer"); des->SetClassName(deserializerString); target->Set(env->context(), deserializerString, des->GetFunction(env->context()).ToLocalChecked()).Check(); } } // anonymous namespace } // namespace node NODE_MODULE_CONTEXT_AWARE_INTERNAL(serdes, node::Initialize)