summaryrefslogtreecommitdiff
path: root/src/node_serdes.cc
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2017-01-26 21:38:11 -0800
committerAnna Henningsen <anna@addaleax.net>2017-03-29 05:14:55 +0200
commit1fde98bb4fa5cab0d060994768ebd055ce6fbf2c (patch)
tree9bac24c149cc8ac88c1edc30ede6ddaa952ae47e /src/node_serdes.cc
parent6d93508369481591ba31f34bddfd95e2cc151edb (diff)
downloadandroid-node-v8-1fde98bb4fa5cab0d060994768ebd055ce6fbf2c.tar.gz
android-node-v8-1fde98bb4fa5cab0d060994768ebd055ce6fbf2c.tar.bz2
android-node-v8-1fde98bb4fa5cab0d060994768ebd055ce6fbf2c.zip
v8: expose new V8 serialization API
Expose the new serialization API that was added in V8 5.5 to userland. The JS API is virtually a direct copy of what V8 provides on the C++ level. This is useful Node as a possible replacement for some internals that currently use JSON, like IPC, but is likely to be useful to general userland code as well. PR-URL: https://github.com/nodejs/node/pull/11048 Reviewed-By: Michaƫl Zasso <targos@protonmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Diffstat (limited to 'src/node_serdes.cc')
-rw-r--r--src/node_serdes.cc483
1 files changed, 483 insertions, 0 deletions
diff --git a/src/node_serdes.cc b/src/node_serdes.cc
new file mode 100644
index 0000000000..144de1695e
--- /dev/null
+++ b/src/node_serdes.cc
@@ -0,0 +1,483 @@
+#include "node.h"
+#include "node_buffer.h"
+#include "base-object.h"
+#include "base-object-inl.h"
+#include "env.h"
+#include "env-inl.h"
+#include "v8.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;
+
+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);
+ 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);
+ 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<SerializerContext>(this);
+}
+
+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());
+
+ Maybe<bool> value = args[0]->BooleanValue(ctx->env()->context());
+ if (value.IsNothing()) return;
+ ctx->serializer_.SetTreatArrayBufferViewsAsHostObjects(value.FromJust());
+}
+
+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 ctx->env()->ThrowTypeError("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]->IsUint8Array()) {
+ return ctx->env()->ThrowTypeError("source must be a Uint8Array");
+ }
+
+ 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);
+
+ MakeWeak<DeserializerContext>(this);
+}
+
+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);
+ }
+
+ 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]->IsUint8Array()) {
+ return env->ThrowTypeError("buffer must be a Uint8Array");
+ }
+
+ 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 ctx->env()->ThrowTypeError("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<Context> context = ctx->env()->context();
+
+ Local<Array> ret = Array::New(isolate, 2);
+ ret->Set(context, 0, Integer::NewFromUnsigned(isolate, hi));
+ ret->Set(context, 1, Integer::NewFromUnsigned(isolate, lo));
+ return args.GetReturnValue().Set(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 InitializeSerdesBindings(Local<Object> target,
+ Local<Value> unused,
+ Local<Context> context) {
+ 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);
+
+ ser->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Serializer"));
+ target->Set(env->context(),
+ FIXED_ONE_BYTE_STRING(env->isolate(), "Serializer"),
+ 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);
+
+ des->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Deserializer"));
+ target->Set(env->context(),
+ FIXED_ONE_BYTE_STRING(env->isolate(), "Deserializer"),
+ des->GetFunction(env->context()).ToLocalChecked()).FromJust();
+}
+
+} // namespace node
+
+NODE_MODULE_CONTEXT_AWARE_BUILTIN(serdes, node::InitializeSerdesBindings)