| #include "node_internals.h" |
| #include "node_buffer.h" |
| #include "node_errors.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<Object> wrap); |
| |
| ~SerializerContext() {} |
| |
| void ThrowDataCloneError(Local<String> message) override; |
| Maybe<bool> WriteHostObject(Isolate* isolate, Local<Object> object) override; |
| Maybe<uint32_t> GetSharedArrayBufferId( |
| Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) override; |
| |
| static void SetTreatArrayBufferViewsAsHostObjects( |
| const FunctionCallbackInfo<Value>& args); |
| |
| static void New(const FunctionCallbackInfo<Value>& args); |
| static void WriteHeader(const FunctionCallbackInfo<Value>& args); |
| static void WriteValue(const FunctionCallbackInfo<Value>& args); |
| static void ReleaseBuffer(const FunctionCallbackInfo<Value>& args); |
| static void TransferArrayBuffer(const FunctionCallbackInfo<Value>& args); |
| static void WriteUint32(const FunctionCallbackInfo<Value>& args); |
| static void WriteUint64(const FunctionCallbackInfo<Value>& args); |
| static void WriteDouble(const FunctionCallbackInfo<Value>& args); |
| static void WriteRawBytes(const FunctionCallbackInfo<Value>& 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<Object> wrap, |
| Local<Value> buffer); |
| |
| ~DeserializerContext() {} |
| |
| MaybeLocal<Object> ReadHostObject(Isolate* isolate) override; |
| |
| static void New(const FunctionCallbackInfo<Value>& args); |
| static void ReadHeader(const FunctionCallbackInfo<Value>& args); |
| static void ReadValue(const FunctionCallbackInfo<Value>& args); |
| static void TransferArrayBuffer(const FunctionCallbackInfo<Value>& args); |
| static void GetWireFormatVersion(const FunctionCallbackInfo<Value>& args); |
| static void ReadUint32(const FunctionCallbackInfo<Value>& args); |
| static void ReadUint64(const FunctionCallbackInfo<Value>& args); |
| static void ReadDouble(const FunctionCallbackInfo<Value>& args); |
| static void ReadRawBytes(const FunctionCallbackInfo<Value>& 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<Object> wrap) |
| : BaseObject(env, wrap), |
| serializer_(env->isolate(), this) { |
| MakeWeak(); |
| } |
| |
| void SerializerContext::ThrowDataCloneError(Local<String> message) { |
| Local<Value> args[1] = { message }; |
| Local<Value> get_data_clone_error = |
| object()->Get(env()->context(), |
| env()->get_data_clone_error_string()) |
| .ToLocalChecked(); |
| |
| CHECK(get_data_clone_error->IsFunction()); |
| MaybeLocal<Value> error = |
| get_data_clone_error.As<Function>()->Call(env()->context(), |
| object(), |
| arraysize(args), |
| args); |
| |
| if (error.IsEmpty()) return; |
| |
| env()->isolate()->ThrowException(error.ToLocalChecked()); |
| } |
| |
| Maybe<uint32_t> SerializerContext::GetSharedArrayBufferId( |
| Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) { |
| Local<Value> args[1] = { shared_array_buffer }; |
| Local<Value> 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<Value> id = |
| get_shared_array_buffer_id.As<Function>()->Call(env()->context(), |
| object(), |
| arraysize(args), |
| args); |
| |
| if (id.IsEmpty()) return Nothing<uint32_t>(); |
| |
| return id.ToLocalChecked()->Uint32Value(env()->context()); |
| } |
| |
| Maybe<bool> SerializerContext::WriteHostObject(Isolate* isolate, |
| Local<Object> input) { |
| MaybeLocal<Value> ret; |
| Local<Value> args[1] = { input }; |
| |
| Local<Value> 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<Function>()->Call(env()->context(), |
| object(), |
| arraysize(args), |
| args); |
| |
| if (ret.IsEmpty()) |
| return Nothing<bool>(); |
| |
| return Just(true); |
| } |
| |
| void SerializerContext::New(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| |
| new SerializerContext(env, args.This()); |
| } |
| |
| void SerializerContext::WriteHeader(const FunctionCallbackInfo<Value>& args) { |
| SerializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| ctx->serializer_.WriteHeader(); |
| } |
| |
| void SerializerContext::WriteValue(const FunctionCallbackInfo<Value>& args) { |
| SerializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| Maybe<bool> ret = |
| ctx->serializer_.WriteValue(ctx->env()->context(), args[0]); |
| |
| if (ret.IsJust()) args.GetReturnValue().Set(ret.FromJust()); |
| } |
| |
| void SerializerContext::SetTreatArrayBufferViewsAsHostObjects( |
| const FunctionCallbackInfo<Value>& 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<Value>& args) { |
| SerializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| |
| std::pair<uint8_t*, size_t> ret = ctx->serializer_.Release(); |
| auto buf = Buffer::New(ctx->env(), |
| reinterpret_cast<char*>(ret.first), |
| ret.second); |
| |
| if (!buf.IsEmpty()) { |
| args.GetReturnValue().Set(buf.ToLocalChecked()); |
| } |
| } |
| |
| void SerializerContext::TransferArrayBuffer( |
| const FunctionCallbackInfo<Value>& args) { |
| SerializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| |
| Maybe<uint32_t> 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<ArrayBuffer> ab = args[1].As<ArrayBuffer>(); |
| ctx->serializer_.TransferArrayBuffer(id.FromJust(), ab); |
| return; |
| } |
| |
| void SerializerContext::WriteUint32(const FunctionCallbackInfo<Value>& args) { |
| SerializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| |
| Maybe<uint32_t> value = args[0]->Uint32Value(ctx->env()->context()); |
| if (value.IsNothing()) return; |
| |
| ctx->serializer_.WriteUint32(value.FromJust()); |
| } |
| |
| void SerializerContext::WriteUint64(const FunctionCallbackInfo<Value>& args) { |
| SerializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| |
| Maybe<uint32_t> arg0 = args[0]->Uint32Value(ctx->env()->context()); |
| Maybe<uint32_t> 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<Value>& args) { |
| SerializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| |
| Maybe<double> value = args[0]->NumberValue(ctx->env()->context()); |
| if (value.IsNothing()) return; |
| |
| ctx->serializer_.WriteDouble(value.FromJust()); |
| } |
| |
| void SerializerContext::WriteRawBytes(const FunctionCallbackInfo<Value>& 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"); |
| } |
| |
| ctx->serializer_.WriteRawBytes(Buffer::Data(args[0]), |
| Buffer::Length(args[0])); |
| } |
| |
| DeserializerContext::DeserializerContext(Environment* env, |
| Local<Object> wrap, |
| Local<Value> buffer) |
| : BaseObject(env, wrap), |
| data_(reinterpret_cast<const uint8_t*>(Buffer::Data(buffer))), |
| length_(Buffer::Length(buffer)), |
| deserializer_(env->isolate(), data_, length_, this) { |
| object()->Set(env->context(), env->buffer_string(), buffer).FromJust(); |
| deserializer_.SetExpectInlineWasm(true); |
| |
| MakeWeak(); |
| } |
| |
| MaybeLocal<Object> DeserializerContext::ReadHostObject(Isolate* isolate) { |
| Local<Value> 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<Value> ret = |
| read_host_object.As<Function>()->Call(env()->context(), |
| object(), |
| 0, |
| nullptr); |
| |
| if (ret.IsEmpty()) |
| return MaybeLocal<Object>(); |
| |
| Local<Value> return_value = ret.ToLocalChecked(); |
| if (!return_value->IsObject()) { |
| env()->ThrowTypeError("readHostObject must return an object"); |
| return MaybeLocal<Object>(); |
| } |
| |
| return return_value.As<Object>(); |
| } |
| |
| void DeserializerContext::New(const FunctionCallbackInfo<Value>& 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<Value>& args) { |
| DeserializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| |
| Maybe<bool> ret = ctx->deserializer_.ReadHeader(ctx->env()->context()); |
| |
| if (ret.IsJust()) args.GetReturnValue().Set(ret.FromJust()); |
| } |
| |
| void DeserializerContext::ReadValue(const FunctionCallbackInfo<Value>& args) { |
| DeserializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| |
| MaybeLocal<Value> ret = ctx->deserializer_.ReadValue(ctx->env()->context()); |
| |
| if (!ret.IsEmpty()) args.GetReturnValue().Set(ret.ToLocalChecked()); |
| } |
| |
| void DeserializerContext::TransferArrayBuffer( |
| const FunctionCallbackInfo<Value>& args) { |
| DeserializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| |
| Maybe<uint32_t> id = args[0]->Uint32Value(ctx->env()->context()); |
| if (id.IsNothing()) return; |
| |
| if (args[1]->IsArrayBuffer()) { |
| Local<ArrayBuffer> ab = args[1].As<ArrayBuffer>(); |
| ctx->deserializer_.TransferArrayBuffer(id.FromJust(), ab); |
| return; |
| } |
| |
| if (args[1]->IsSharedArrayBuffer()) { |
| Local<SharedArrayBuffer> sab = args[1].As<SharedArrayBuffer>(); |
| 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<Value>& args) { |
| DeserializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| |
| args.GetReturnValue().Set(ctx->deserializer_.GetWireFormatVersion()); |
| } |
| |
| void DeserializerContext::ReadUint32(const FunctionCallbackInfo<Value>& 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<Value>& 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<uint32_t>(value >> 32); |
| uint32_t lo = static_cast<uint32_t>(value); |
| |
| Isolate* isolate = ctx->env()->isolate(); |
| |
| Local<Value> ret[] = { |
| Integer::NewFromUnsigned(isolate, hi), |
| Integer::NewFromUnsigned(isolate, lo) |
| }; |
| return args.GetReturnValue().Set(Array::New(isolate, ret, arraysize(ret))); |
| } |
| |
| void DeserializerContext::ReadDouble(const FunctionCallbackInfo<Value>& 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<Value>& args) { |
| DeserializerContext* ctx; |
| ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); |
| |
| Maybe<int64_t> 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<const uint8_t*>(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<Object> target, |
| Local<Value> unused, |
| Local<Context> context, |
| void* priv) { |
| Environment* env = Environment::GetCurrent(context); |
| Local<FunctionTemplate> 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<String> serializerString = |
| FIXED_ONE_BYTE_STRING(env->isolate(), "Serializer"); |
| ser->SetClassName(serializerString); |
| target->Set(env->context(), |
| serializerString, |
| ser->GetFunction(env->context()).ToLocalChecked()).FromJust(); |
| |
| Local<FunctionTemplate> 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<String> deserializerString = |
| FIXED_ONE_BYTE_STRING(env->isolate(), "Deserializer"); |
| des->SetClassName(deserializerString); |
| target->Set(env->context(), |
| deserializerString, |
| des->GetFunction(env->context()).ToLocalChecked()).FromJust(); |
| } |
| |
| } // anonymous namespace |
| } // namespace node |
| |
| NODE_MODULE_CONTEXT_AWARE_INTERNAL(serdes, node::Initialize) |