node/src/node_serdes.cc
Joyee Cheung 5c86f223ae
src: use the internal field to determine if an object is a BaseObject
Instead of storing the function template of BaseObject for checking
if an object is BaseObject by calling HasInstance, simply checks
the first internal field of the object, which is always set in the
BaseObject constructor. This is simpler and faster
(there is now no need to iterate over the inheritance for the check).

PR-URL: https://github.com/nodejs/node/pull/47217
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
2023-04-06 00:24:48 +00:00

543 lines
18 KiB
C++

#include "base_object-inl.h"
#include "node_buffer.h"
#include "node_errors.h"
#include "node_external_reference.h"
#include "node_internals.h"
#include "util-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 serdes {
class SerializerContext : public BaseObject,
public ValueSerializer::Delegate {
public:
SerializerContext(Environment* env,
Local<Object> wrap);
~SerializerContext() override = default;
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() override = default;
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);
if (!args.IsConstructCall()) {
return THROW_ERR_CONSTRUCT_CALL_REQUIRED(
env, "Class constructor Serializer cannot be invoked without 'new'");
}
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());
// Note: Both ValueSerializer and this Buffer::New() variant use malloc()
// as the underlying allocator.
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");
}
ArrayBufferViewContents<char> bytes(args[0]);
ctx->serializer_.WriteRawBytes(bytes.data(), bytes.length());
}
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).Check();
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.IsConstructCall()) {
return THROW_ERR_CONSTRUCT_CALL_REQUIRED(
env, "Class constructor Deserializer cannot be invoked without 'new'");
}
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 = static_cast<uint32_t>(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);
Isolate* isolate = env->isolate();
Local<FunctionTemplate> ser =
NewFunctionTemplate(isolate, SerializerContext::New);
ser->InstanceTemplate()->SetInternalFieldCount(
SerializerContext::kInternalFieldCount);
SetProtoMethod(isolate, ser, "writeHeader", SerializerContext::WriteHeader);
SetProtoMethod(isolate, ser, "writeValue", SerializerContext::WriteValue);
SetProtoMethod(
isolate, ser, "releaseBuffer", SerializerContext::ReleaseBuffer);
SetProtoMethod(isolate,
ser,
"transferArrayBuffer",
SerializerContext::TransferArrayBuffer);
SetProtoMethod(isolate, ser, "writeUint32", SerializerContext::WriteUint32);
SetProtoMethod(isolate, ser, "writeUint64", SerializerContext::WriteUint64);
SetProtoMethod(isolate, ser, "writeDouble", SerializerContext::WriteDouble);
SetProtoMethod(
isolate, ser, "writeRawBytes", SerializerContext::WriteRawBytes);
SetProtoMethod(isolate,
ser,
"_setTreatArrayBufferViewsAsHostObjects",
SerializerContext::SetTreatArrayBufferViewsAsHostObjects);
ser->ReadOnlyPrototype();
SetConstructorFunction(context, target, "Serializer", ser);
Local<FunctionTemplate> des =
NewFunctionTemplate(isolate, DeserializerContext::New);
des->InstanceTemplate()->SetInternalFieldCount(
DeserializerContext::kInternalFieldCount);
SetProtoMethod(isolate, des, "readHeader", DeserializerContext::ReadHeader);
SetProtoMethod(isolate, des, "readValue", DeserializerContext::ReadValue);
SetProtoMethod(isolate,
des,
"getWireFormatVersion",
DeserializerContext::GetWireFormatVersion);
SetProtoMethod(isolate,
des,
"transferArrayBuffer",
DeserializerContext::TransferArrayBuffer);
SetProtoMethod(isolate, des, "readUint32", DeserializerContext::ReadUint32);
SetProtoMethod(isolate, des, "readUint64", DeserializerContext::ReadUint64);
SetProtoMethod(isolate, des, "readDouble", DeserializerContext::ReadDouble);
SetProtoMethod(
isolate, des, "_readRawBytes", DeserializerContext::ReadRawBytes);
des->SetLength(1);
des->ReadOnlyPrototype();
SetConstructorFunction(context, target, "Deserializer", des);
}
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(SerializerContext::New);
registry->Register(SerializerContext::WriteHeader);
registry->Register(SerializerContext::WriteValue);
registry->Register(SerializerContext::ReleaseBuffer);
registry->Register(SerializerContext::TransferArrayBuffer);
registry->Register(SerializerContext::WriteUint32);
registry->Register(SerializerContext::WriteUint64);
registry->Register(SerializerContext::WriteDouble);
registry->Register(SerializerContext::WriteRawBytes);
registry->Register(SerializerContext::SetTreatArrayBufferViewsAsHostObjects);
registry->Register(DeserializerContext::New);
registry->Register(DeserializerContext::ReadHeader);
registry->Register(DeserializerContext::ReadValue);
registry->Register(DeserializerContext::GetWireFormatVersion);
registry->Register(DeserializerContext::TransferArrayBuffer);
registry->Register(DeserializerContext::ReadUint32);
registry->Register(DeserializerContext::ReadUint64);
registry->Register(DeserializerContext::ReadDouble);
registry->Register(DeserializerContext::ReadRawBytes);
}
} // namespace serdes
} // namespace node
NODE_BINDING_CONTEXT_AWARE_INTERNAL(serdes, node::serdes::Initialize)
NODE_BINDING_EXTERNAL_REFERENCE(serdes,
node::serdes::RegisterExternalReferences)