diff --git a/src/node_blob.cc b/src/node_blob.cc index d5475de36d7738..970117efc3dc1f 100644 --- a/src/node_blob.cc +++ b/src/node_blob.cc @@ -41,7 +41,10 @@ namespace { // Concatenate multiple ArrayBufferView/ArrayBuffers into a single ArrayBuffer. // This method treats all ArrayBufferView types the same. void Concat(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + Environment* env = Environment::GetCurrent(context); + CHECK(args[0]->IsArray()); Local array = args[0].As(); @@ -54,9 +57,14 @@ void Concat(const FunctionCallbackInfo& args) { std::vector views; size_t total = 0; - for (uint32_t n = 0; n < array->Length(); n++) { - Local val; - if (!array->Get(env->context(), n).ToLocal(&val)) return; + std::vector> buffers; + if (FromV8Array(context, array, &buffers).IsNothing()) { + return; + } + + size_t count = buffers.size(); + for (uint32_t i = 0; i < count; i++) { + Local val = buffers[i].Get(isolate); if (val->IsArrayBuffer()) { auto ab = val.As(); views.push_back(View{ab->GetBackingStore(), ab->ByteLength(), 0}); @@ -169,21 +177,27 @@ BaseObjectPtr Blob::Create(Environment* env, } void Blob::New(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + Environment* env = Environment::GetCurrent(context); + CHECK(args[0]->IsArray()); // sources Local array = args[0].As(); std::vector> entries(array->Length()); - for (size_t i = 0; i < array->Length(); i++) { - Local entry; - if (!array->Get(env->context(), i).ToLocal(&entry)) { - return; - } + std::vector> sources; + if (FromV8Array(context, array, &sources).IsNothing()) { + return; + } + + size_t count = sources.size(); + for (size_t i = 0; i < count; i++) { + Local entry = sources[i].Get(isolate); - const auto entryFromArrayBuffer = [env](v8::Local buf, - size_t byte_length, - size_t byte_offset = 0) { + const auto entryFromArrayBuffer = [isolate](v8::Local buf, + size_t byte_length, + size_t byte_offset = 0) { if (buf->IsDetachable()) { std::shared_ptr store = buf->GetBackingStore(); USE(buf->Detach(Local())); @@ -193,7 +207,7 @@ void Blob::New(const FunctionCallbackInfo& args) { // If the ArrayBuffer is not detachable, we will copy from it instead. std::shared_ptr store = - ArrayBuffer::NewBackingStore(env->isolate(), byte_length); + ArrayBuffer::NewBackingStore(isolate, byte_length); uint8_t* ptr = static_cast(buf->Data()) + byte_offset; std::copy(ptr, ptr + byte_length, static_cast(store->Data())); return DataQueue::CreateInMemoryEntryFromBackingStore( diff --git a/src/util-inl.h b/src/util-inl.h index 864f6d86cdf689..80444d73a92fd5 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -401,6 +401,29 @@ inline char* UncheckedCalloc(size_t n) { return UncheckedCalloc(n); } // headers than we really need to. void ThrowErrStringTooLong(v8::Isolate* isolate); +struct ArrayIterationData { + std::vector>* out; + v8::Isolate* isolate = nullptr; +}; + +inline v8::Array::CallbackResult PushItemToVector(uint32_t index, + v8::Local element, + void* data) { + auto vec = static_cast(data)->out; + auto isolate = static_cast(data)->isolate; + vec->push_back(v8::Global(isolate, element)); + return v8::Array::CallbackResult::kContinue; +} + +v8::Maybe FromV8Array(v8::Local context, + v8::Local js_array, + std::vector>* out) { + uint32_t count = js_array->Length(); + out->reserve(count); + ArrayIterationData data{out, context->GetIsolate()}; + return js_array->Iterate(context, PushItemToVector, &data); +} + v8::MaybeLocal ToV8Value(v8::Local context, std::string_view str, v8::Isolate* isolate) { diff --git a/src/util.h b/src/util.h index 83e5d226701b9b..a9d1ec039e8517 100644 --- a/src/util.h +++ b/src/util.h @@ -697,6 +697,16 @@ struct FunctionDeleter { template using DeleteFnPtr = typename FunctionDeleter::Pointer; +// Convert a v8::Array into an std::vector using the callback-based API. +// This can be faster than calling Array::Get() repeatedly when the array +// has more than 2 entries. +// Note that iterating over an array in C++ and performing operations on each +// element in a C++ loop is still slower than iterating over the array in JS +// and calling into native in the JS loop repeatedly on each element, +// as of V8 11.9. +inline v8::Maybe FromV8Array(v8::Local context, + v8::Local js_array, + std::vector>* out); std::vector SplitString(const std::string_view in, const std::string_view delim);