%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/node_blob.cc |
#include "node_blob.h"
#include "ada.h"
#include "async_wrap-inl.h"
#include "base_object-inl.h"
#include "env-inl.h"
#include "memory_tracker-inl.h"
#include "node_bob-inl.h"
#include "node_errors.h"
#include "node_external_reference.h"
#include "node_file.h"
#include "permission/permission.h"
#include "util.h"
#include "v8.h"
#include <algorithm>
namespace node {
using v8::Array;
using v8::ArrayBuffer;
using v8::ArrayBufferView;
using v8::BackingStore;
using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Global;
using v8::HandleScope;
using v8::Int32;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::ObjectTemplate;
using v8::String;
using v8::Uint32;
using v8::Undefined;
using v8::Value;
namespace {
// Concatenate multiple ArrayBufferView/ArrayBuffers into a single ArrayBuffer.
// This method treats all ArrayBufferView types the same.
void Concat(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Environment* env = Environment::GetCurrent(context);
CHECK(args[0]->IsArray());
Local<Array> array = args[0].As<Array>();
struct View {
std::shared_ptr<BackingStore> store;
size_t length;
size_t offset = 0;
};
std::vector<View> views;
size_t total = 0;
std::vector<v8::Global<Value>> buffers;
if (FromV8Array(context, array, &buffers).IsNothing()) {
return;
}
size_t count = buffers.size();
for (uint32_t i = 0; i < count; i++) {
Local<Value> val = buffers[i].Get(isolate);
if (val->IsArrayBuffer()) {
auto ab = val.As<ArrayBuffer>();
views.push_back(View{ab->GetBackingStore(), ab->ByteLength(), 0});
total += ab->ByteLength();
} else {
CHECK(val->IsArrayBufferView());
auto view = val.As<ArrayBufferView>();
views.push_back(View{view->Buffer()->GetBackingStore(),
view->ByteLength(),
view->ByteOffset()});
total += view->ByteLength();
}
}
std::shared_ptr<BackingStore> store =
ArrayBuffer::NewBackingStore(env->isolate(), total);
uint8_t* ptr = static_cast<uint8_t*>(store->Data());
for (size_t n = 0; n < views.size(); n++) {
uint8_t* from =
static_cast<uint8_t*>(views[n].store->Data()) + views[n].offset;
std::copy(from, from + views[n].length, ptr);
ptr += views[n].length;
}
args.GetReturnValue().Set(ArrayBuffer::New(env->isolate(), std::move(store)));
}
void BlobFromFilePath(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
BufferValue path(env->isolate(), args[0]);
CHECK_NOT_NULL(*path);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
auto entry = DataQueue::CreateFdEntry(env, args[0]);
if (entry == nullptr) {
return THROW_ERR_INVALID_ARG_VALUE(env, "Unabled to open file as blob");
}
std::vector<std::unique_ptr<DataQueue::Entry>> entries;
entries.push_back(std::move(entry));
auto blob =
Blob::Create(env, DataQueue::CreateIdempotent(std::move(entries)));
if (blob) {
auto array = Array::New(env->isolate(), 2);
USE(array->Set(env->context(), 0, blob->object()));
USE(array->Set(env->context(),
1,
Uint32::NewFromUnsigned(env->isolate(), blob->length())));
args.GetReturnValue().Set(array);
}
}
} // namespace
void Blob::CreatePerIsolateProperties(IsolateData* isolate_data,
Local<ObjectTemplate> target) {
Isolate* isolate = isolate_data->isolate();
SetMethod(isolate, target, "createBlob", New);
SetMethod(isolate, target, "storeDataObject", StoreDataObject);
SetMethod(isolate, target, "getDataObject", GetDataObject);
SetMethod(isolate, target, "revokeObjectURL", RevokeObjectURL);
SetMethod(isolate, target, "concat", Concat);
SetMethod(isolate, target, "createBlobFromFilePath", BlobFromFilePath);
}
void Blob::CreatePerContextProperties(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Realm* realm = Realm::GetCurrent(context);
realm->AddBindingData<BlobBindingData>(target);
}
Local<FunctionTemplate> Blob::GetConstructorTemplate(Environment* env) {
Local<FunctionTemplate> tmpl = env->blob_constructor_template();
if (tmpl.IsEmpty()) {
Isolate* isolate = env->isolate();
tmpl = NewFunctionTemplate(isolate, nullptr);
tmpl->InstanceTemplate()->SetInternalFieldCount(
BaseObject::kInternalFieldCount);
tmpl->SetClassName(
FIXED_ONE_BYTE_STRING(env->isolate(), "Blob"));
SetProtoMethod(isolate, tmpl, "getReader", GetReader);
SetProtoMethod(isolate, tmpl, "slice", ToSlice);
env->set_blob_constructor_template(tmpl);
}
return tmpl;
}
bool Blob::HasInstance(Environment* env, v8::Local<v8::Value> object) {
return GetConstructorTemplate(env)->HasInstance(object);
}
BaseObjectPtr<Blob> Blob::Create(Environment* env,
std::shared_ptr<DataQueue> data_queue) {
HandleScope scope(env->isolate());
Local<Function> ctor;
if (!GetConstructorTemplate(env)->GetFunction(env->context()).ToLocal(&ctor))
return BaseObjectPtr<Blob>();
Local<Object> obj;
if (!ctor->NewInstance(env->context()).ToLocal(&obj))
return BaseObjectPtr<Blob>();
return MakeBaseObject<Blob>(env, obj, data_queue);
}
void Blob::New(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Environment* env = Environment::GetCurrent(context);
CHECK(args[0]->IsArray()); // sources
Local<Array> array = args[0].As<Array>();
std::vector<std::unique_ptr<DataQueue::Entry>> entries(array->Length());
std::vector<v8::Global<Value>> sources;
if (FromV8Array(context, array, &sources).IsNothing()) {
return;
}
size_t count = sources.size();
for (size_t i = 0; i < count; i++) {
Local<Value> entry = sources[i].Get(isolate);
const auto entryFromArrayBuffer = [isolate](v8::Local<v8::ArrayBuffer> buf,
size_t byte_length,
size_t byte_offset = 0) {
if (buf->IsDetachable()) {
std::shared_ptr<BackingStore> store = buf->GetBackingStore();
USE(buf->Detach(Local<Value>()));
return DataQueue::CreateInMemoryEntryFromBackingStore(
store, byte_offset, byte_length);
}
// If the ArrayBuffer is not detachable, we will copy from it instead.
std::shared_ptr<BackingStore> store =
ArrayBuffer::NewBackingStore(isolate, byte_length);
uint8_t* ptr = static_cast<uint8_t*>(buf->Data()) + byte_offset;
std::copy(ptr, ptr + byte_length, static_cast<uint8_t*>(store->Data()));
return DataQueue::CreateInMemoryEntryFromBackingStore(
store, 0, byte_length);
};
// Every entry should be either an ArrayBuffer, ArrayBufferView, or Blob.
// If the input to the Blob constructor in JavaScript was a string, then
// it will be decoded into an ArrayBufferView there before being passed
// in.
//
// Importantly, here we also assume that the ArrayBuffer/ArrayBufferView
// is not going to be modified here so we will detach them. It is up to
// the JavaScript side to do the right thing with regards to copying and
// ensuring appropriate spec compliance.
if (entry->IsArrayBuffer()) {
Local<ArrayBuffer> buf = entry.As<ArrayBuffer>();
entries[i] = entryFromArrayBuffer(buf, buf->ByteLength());
} else if (entry->IsArrayBufferView()) {
Local<ArrayBufferView> view = entry.As<ArrayBufferView>();
entries[i] = entryFromArrayBuffer(
view->Buffer(), view->ByteLength(), view->ByteOffset());
} else if (Blob::HasInstance(env, entry)) {
Blob* blob;
ASSIGN_OR_RETURN_UNWRAP(&blob, entry);
entries[i] = DataQueue::CreateDataQueueEntry(blob->data_queue_);
} else {
UNREACHABLE("Incorrect Blob initialization type");
}
}
auto blob = Create(env, DataQueue::CreateIdempotent(std::move(entries)));
if (blob)
args.GetReturnValue().Set(blob->object());
}
void Blob::GetReader(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Blob* blob;
ASSIGN_OR_RETURN_UNWRAP(&blob, args.Holder());
BaseObjectPtr<Blob::Reader> reader =
Blob::Reader::Create(env, BaseObjectPtr<Blob>(blob));
if (reader) args.GetReturnValue().Set(reader->object());
}
void Blob::ToSlice(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Blob* blob;
ASSIGN_OR_RETURN_UNWRAP(&blob, args.Holder());
CHECK(args[0]->IsUint32());
CHECK(args[1]->IsUint32());
size_t start = args[0].As<Uint32>()->Value();
size_t end = args[1].As<Uint32>()->Value();
BaseObjectPtr<Blob> slice = blob->Slice(env, start, end);
if (slice)
args.GetReturnValue().Set(slice->object());
}
void Blob::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("data_queue_", data_queue_, "std::shared_ptr<DataQueue>");
}
BaseObjectPtr<Blob> Blob::Slice(Environment* env, size_t start, size_t end) {
return Create(env,
this->data_queue_->slice(start, static_cast<uint64_t>(end)));
}
Blob::Blob(Environment* env,
v8::Local<v8::Object> obj,
std::shared_ptr<DataQueue> data_queue)
: BaseObject(env, obj), data_queue_(data_queue) {
MakeWeak();
}
Blob::Reader::Reader(Environment* env,
v8::Local<v8::Object> obj,
BaseObjectPtr<Blob> strong_ptr)
: AsyncWrap(env, obj, AsyncWrap::PROVIDER_BLOBREADER),
inner_(strong_ptr->data_queue_->get_reader()),
strong_ptr_(std::move(strong_ptr)) {
MakeWeak();
}
bool Blob::Reader::HasInstance(Environment* env, v8::Local<v8::Value> value) {
return GetConstructorTemplate(env)->HasInstance(value);
}
Local<FunctionTemplate> Blob::Reader::GetConstructorTemplate(Environment* env) {
Local<FunctionTemplate> tmpl = env->blob_reader_constructor_template();
if (tmpl.IsEmpty()) {
Isolate* isolate = env->isolate();
tmpl = NewFunctionTemplate(isolate, nullptr);
tmpl->InstanceTemplate()->SetInternalFieldCount(
BaseObject::kInternalFieldCount);
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "BlobReader"));
SetProtoMethod(env->isolate(), tmpl, "pull", Pull);
env->set_blob_reader_constructor_template(tmpl);
}
return tmpl;
}
BaseObjectPtr<Blob::Reader> Blob::Reader::Create(Environment* env,
BaseObjectPtr<Blob> blob) {
Local<Object> obj;
if (!GetConstructorTemplate(env)
->InstanceTemplate()
->NewInstance(env->context())
.ToLocal(&obj)) {
return BaseObjectPtr<Blob::Reader>();
}
return MakeBaseObject<Blob::Reader>(env, obj, std::move(blob));
}
void Blob::Reader::Pull(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Blob::Reader* reader;
ASSIGN_OR_RETURN_UNWRAP(&reader, args.Holder());
CHECK(args[0]->IsFunction());
Local<Function> fn = args[0].As<Function>();
CHECK(!fn->IsConstructor());
if (reader->eos_) {
Local<Value> arg = Int32::New(env->isolate(), bob::STATUS_EOS);
reader->MakeCallback(fn, 1, &arg);
return args.GetReturnValue().Set(bob::STATUS_EOS);
}
struct Impl {
BaseObjectPtr<Blob::Reader> reader;
Global<Function> callback;
Environment* env;
};
// TODO(@jasnell): A unique_ptr is likely better here but making this a unique
// pointer that is passed into the lambda causes the std::move(next) below to
// complain about std::function needing to be copy-constructible.
Impl* impl = new Impl();
impl->reader = BaseObjectPtr<Blob::Reader>(reader);
impl->callback.Reset(env->isolate(), fn);
impl->env = env;
auto next = [impl](int status,
const DataQueue::Vec* vecs,
size_t count,
bob::Done doneCb) mutable {
auto dropMe = std::unique_ptr<Impl>(impl);
Environment* env = impl->env;
HandleScope handleScope(env->isolate());
Local<Function> fn = impl->callback.Get(env->isolate());
if (status == bob::STATUS_EOS) impl->reader->eos_ = true;
if (count > 0) {
// Copy the returns vectors into a single ArrayBuffer.
size_t total = 0;
for (size_t n = 0; n < count; n++) total += vecs[n].len;
std::shared_ptr<BackingStore> store =
v8::ArrayBuffer::NewBackingStore(env->isolate(), total);
auto ptr = static_cast<uint8_t*>(store->Data());
for (size_t n = 0; n < count; n++) {
std::copy(vecs[n].base, vecs[n].base + vecs[n].len, ptr);
ptr += vecs[n].len;
}
// Since we copied the data buffers, signal that we're done with them.
std::move(doneCb)(0);
Local<Value> argv[2] = {Uint32::New(env->isolate(), status),
ArrayBuffer::New(env->isolate(), store)};
impl->reader->MakeCallback(fn, arraysize(argv), argv);
return;
}
Local<Value> argv[2] = {
Int32::New(env->isolate(), status),
Undefined(env->isolate()),
};
impl->reader->MakeCallback(fn, arraysize(argv), argv);
};
args.GetReturnValue().Set(reader->inner_->Pull(
std::move(next), node::bob::OPTIONS_END, nullptr, 0));
}
BaseObjectPtr<BaseObject>
Blob::BlobTransferData::Deserialize(
Environment* env,
Local<Context> context,
std::unique_ptr<worker::TransferData> self) {
if (context != env->context()) {
THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env);
return {};
}
return Blob::Create(env, data_queue);
}
BaseObject::TransferMode Blob::GetTransferMode() const {
return TransferMode::kCloneable;
}
std::unique_ptr<worker::TransferData> Blob::CloneForMessaging() const {
return std::make_unique<BlobTransferData>(data_queue_);
}
void Blob::StoreDataObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
Realm* realm = Realm::GetCurrent(args);
CHECK(args[0]->IsString()); // ID key
CHECK(Blob::HasInstance(realm->env(), args[1])); // Blob
CHECK(args[2]->IsUint32()); // Length
CHECK(args[3]->IsString()); // Type
BlobBindingData* binding_data = realm->GetBindingData<BlobBindingData>();
Isolate* isolate = realm->isolate();
Utf8Value key(isolate, args[0]);
Blob* blob;
ASSIGN_OR_RETURN_UNWRAP(&blob, args[1]);
size_t length = args[2].As<Uint32>()->Value();
Utf8Value type(isolate, args[3]);
binding_data->store_data_object(
std::string(*key, key.length()),
BlobBindingData::StoredDataObject(
BaseObjectPtr<Blob>(blob),
length,
std::string(*type, type.length())));
}
// TODO(@anonrig): Add V8 Fast API to the following function
void Blob::RevokeObjectURL(const FunctionCallbackInfo<Value>& args) {
CHECK_GE(args.Length(), 1);
CHECK(args[0]->IsString());
Realm* realm = Realm::GetCurrent(args);
BlobBindingData* binding_data = realm->GetBindingData<BlobBindingData>();
Isolate* isolate = realm->isolate();
Utf8Value input(isolate, args[0].As<String>());
auto out = ada::parse<ada::url_aggregator>(input.ToStringView());
if (!out) {
return;
}
auto pathname = out->get_pathname();
auto start_index = pathname.find(':');
if (start_index != std::string_view::npos && start_index != pathname.size()) {
auto end_index = pathname.find(':', start_index + 1);
if (end_index == std::string_view::npos) {
auto id = std::string(pathname.substr(start_index + 1));
binding_data->revoke_data_object(id);
}
}
}
void Blob::GetDataObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK(args[0]->IsString());
Realm* realm = Realm::GetCurrent(args);
BlobBindingData* binding_data = realm->GetBindingData<BlobBindingData>();
Isolate* isolate = realm->isolate();
Utf8Value key(isolate, args[0]);
BlobBindingData::StoredDataObject stored =
binding_data->get_data_object(std::string(*key, key.length()));
if (stored.blob) {
Local<Value> type;
if (!String::NewFromUtf8(isolate,
stored.type.c_str(),
v8::NewStringType::kNormal,
static_cast<int>(stored.type.length()))
.ToLocal(&type)) {
return;
}
Local<Value> values[] = {stored.blob->object(),
Uint32::NewFromUnsigned(isolate, stored.length),
type};
args.GetReturnValue().Set(Array::New(isolate, values, arraysize(values)));
}
}
void BlobBindingData::StoredDataObject::MemoryInfo(
MemoryTracker* tracker) const {
tracker->TrackField("blob", blob, "BaseObjectPtr<Blob>");
}
BlobBindingData::StoredDataObject::StoredDataObject(
const BaseObjectPtr<Blob>& blob_,
size_t length_,
const std::string& type_)
: blob(blob_),
length(length_),
type(type_) {}
BlobBindingData::BlobBindingData(Realm* realm, Local<Object> wrap)
: SnapshotableObject(realm, wrap, type_int) {
MakeWeak();
}
void BlobBindingData::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("data_objects_",
data_objects_,
"std::unordered_map<std::string, StoredDataObject>");
}
void BlobBindingData::store_data_object(
const std::string& uuid,
const BlobBindingData::StoredDataObject& object) {
data_objects_[uuid] = object;
}
void BlobBindingData::revoke_data_object(const std::string& uuid) {
if (data_objects_.find(uuid) == data_objects_.end()) {
return;
}
data_objects_.erase(uuid);
CHECK_EQ(data_objects_.find(uuid), data_objects_.end());
}
BlobBindingData::StoredDataObject BlobBindingData::get_data_object(
const std::string& uuid) {
auto entry = data_objects_.find(uuid);
if (entry == data_objects_.end())
return BlobBindingData::StoredDataObject {};
return entry->second;
}
void BlobBindingData::Deserialize(Local<Context> context,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_IS_SNAPSHOT_SLOT(index);
HandleScope scope(context->GetIsolate());
Realm* realm = Realm::GetCurrent(context);
BlobBindingData* binding = realm->AddBindingData<BlobBindingData>(holder);
CHECK_NOT_NULL(binding);
}
bool BlobBindingData::PrepareForSerialization(Local<Context> context,
v8::SnapshotCreator* creator) {
// Stored blob objects are not actually persisted.
// Return true because we need to maintain the reference to the binding from
// JS land.
return true;
}
InternalFieldInfoBase* BlobBindingData::Serialize(int index) {
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info =
InternalFieldInfoBase::New<InternalFieldInfo>(type());
return info;
}
void Blob::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(Blob::New);
registry->Register(Blob::GetReader);
registry->Register(Blob::ToSlice);
registry->Register(Blob::StoreDataObject);
registry->Register(Blob::GetDataObject);
registry->Register(Blob::RevokeObjectURL);
registry->Register(Blob::Reader::Pull);
registry->Register(Concat);
registry->Register(BlobFromFilePath);
}
} // namespace node
NODE_BINDING_CONTEXT_AWARE_INTERNAL(blob,
node::Blob::CreatePerContextProperties)
NODE_BINDING_PER_ISOLATE_INIT(blob, node::Blob::CreatePerIsolateProperties)
NODE_BINDING_EXTERNAL_REFERENCE(blob, node::Blob::RegisterExternalReferences)