Skip to content

Commit 4f523c2

Browse files
thangktranaddaleax
authored andcommitted
src: migrate to new V8 ArrayBuffer API
ArrayBuffer without BackingStore will soon be deprecated. Fixes:#30529 PR-URL: #30782 Fixes: #30529 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 2dff8dd commit 4f523c2

File tree

10 files changed

+153
-34
lines changed

10 files changed

+153
-34
lines changed

src/env-inl.h

+13-3
Original file line numberDiff line numberDiff line change
@@ -983,10 +983,20 @@ inline v8::MaybeLocal<v8::Object> AllocatedBuffer::ToBuffer() {
983983
inline v8::Local<v8::ArrayBuffer> AllocatedBuffer::ToArrayBuffer() {
984984
CHECK_NOT_NULL(env_);
985985
uv_buf_t buf = release();
986+
auto callback = [](void* data, size_t length, void* deleter_data){
987+
CHECK_NOT_NULL(deleter_data);
988+
989+
static_cast<v8::ArrayBuffer::Allocator*>(deleter_data)
990+
->Free(data, length);
991+
};
992+
std::unique_ptr<v8::BackingStore> backing =
993+
v8::ArrayBuffer::NewBackingStore(buf.base,
994+
buf.len,
995+
callback,
996+
env_->isolate()
997+
->GetArrayBufferAllocator());
986998
return v8::ArrayBuffer::New(env_->isolate(),
987-
buf.base,
988-
buf.len,
989-
v8::ArrayBufferCreationMode::kInternalized);
999+
std::move(backing));
9901000
}
9911001

9921002
inline void Environment::ThrowError(const char* errmsg) {

src/js_native_api_v8.cc

+11-1
Original file line numberDiff line numberDiff line change
@@ -2612,8 +2612,18 @@ napi_status napi_create_external_arraybuffer(napi_env env,
26122612
CHECK_ARG(env, result);
26132613

26142614
v8::Isolate* isolate = env->isolate;
2615+
// The buffer will be freed with v8impl::ArrayBufferReference::New()
2616+
// below, hence this BackingStore does not need to free the buffer.
2617+
std::unique_ptr<v8::BackingStore> backing =
2618+
v8::ArrayBuffer::NewBackingStore(external_data,
2619+
byte_length,
2620+
[](void*, size_t, void*){},
2621+
nullptr);
26152622
v8::Local<v8::ArrayBuffer> buffer =
2616-
v8::ArrayBuffer::New(isolate, external_data, byte_length);
2623+
v8::ArrayBuffer::New(isolate, std::move(backing));
2624+
// TODO(thangktran): drop this check when V8 is pumped to 8.0 .
2625+
if (!buffer->IsExternal())
2626+
buffer->Externalize(buffer->GetBackingStore());
26172627
v8::Maybe<bool> marked = env->mark_arraybuffer_as_untransferable(buffer);
26182628
CHECK_MAYBE_NOTHING(env, marked, napi_generic_failure);
26192629

src/node_buffer.cc

+41-11
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ namespace node {
4747
namespace Buffer {
4848

4949
using v8::ArrayBuffer;
50-
using v8::ArrayBufferCreationMode;
5150
using v8::ArrayBufferView;
51+
using v8::BackingStore;
5252
using v8::Context;
5353
using v8::EscapableHandleScope;
5454
using v8::FunctionCallbackInfo;
@@ -127,8 +127,8 @@ CallbackInfo::CallbackInfo(Environment* env,
127127
data_(data),
128128
hint_(hint),
129129
env_(env) {
130-
ArrayBuffer::Contents obj_c = object->GetContents();
131-
CHECK_EQ(data_, static_cast<char*>(obj_c.Data()));
130+
std::shared_ptr<BackingStore> obj_backing = object->GetBackingStore();
131+
CHECK_EQ(data_, static_cast<char*>(obj_backing->Data()));
132132
if (object->ByteLength() != 0)
133133
CHECK_NOT_NULL(data_);
134134

@@ -406,7 +406,19 @@ MaybeLocal<Object> New(Environment* env,
406406
return Local<Object>();
407407
}
408408

409-
Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), data, length);
409+
410+
// The buffer will be released by a CallbackInfo::New() below,
411+
// hence this BackingStore callback is empty.
412+
std::unique_ptr<BackingStore> backing =
413+
ArrayBuffer::NewBackingStore(data,
414+
length,
415+
[](void*, size_t, void*){},
416+
nullptr);
417+
Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(),
418+
std::move(backing));
419+
// TODO(thangktran): drop this check when V8 is pumped to 8.0 .
420+
if (!ab->IsExternal())
421+
ab->Externalize(ab->GetBackingStore());
410422
if (ab->SetPrivate(env->context(),
411423
env->arraybuffer_untransferable_private_symbol(),
412424
True(env->isolate())).IsNothing()) {
@@ -465,11 +477,21 @@ MaybeLocal<Object> New(Environment* env,
465477
}
466478
}
467479

468-
Local<ArrayBuffer> ab =
469-
ArrayBuffer::New(env->isolate(),
470-
data,
471-
length,
472-
ArrayBufferCreationMode::kInternalized);
480+
auto callback = [](void* data, size_t length, void* deleter_data){
481+
CHECK_NOT_NULL(deleter_data);
482+
483+
static_cast<v8::ArrayBuffer::Allocator*>(deleter_data)
484+
->Free(data, length);
485+
};
486+
std::unique_ptr<v8::BackingStore> backing =
487+
v8::ArrayBuffer::NewBackingStore(data,
488+
length,
489+
callback,
490+
env->isolate()
491+
->GetArrayBufferAllocator());
492+
Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(),
493+
std::move(backing));
494+
473495
return Buffer::New(env, ab, 0, length).FromMaybe(Local<Object>());
474496
}
475497

@@ -1181,8 +1203,16 @@ void Initialize(Local<Object> target,
11811203
if (NodeArrayBufferAllocator* allocator =
11821204
env->isolate_data()->node_allocator()) {
11831205
uint32_t* zero_fill_field = allocator->zero_fill_field();
1184-
Local<ArrayBuffer> array_buffer = ArrayBuffer::New(
1185-
env->isolate(), zero_fill_field, sizeof(*zero_fill_field));
1206+
std::unique_ptr<BackingStore> backing =
1207+
ArrayBuffer::NewBackingStore(zero_fill_field,
1208+
sizeof(*zero_fill_field),
1209+
[](void*, size_t, void*){},
1210+
nullptr);
1211+
Local<ArrayBuffer> array_buffer =
1212+
ArrayBuffer::New(env->isolate(), std::move(backing));
1213+
// TODO(thangktran): drop this check when V8 is pumped to 8.0 .
1214+
if (!array_buffer->IsExternal())
1215+
array_buffer->Externalize(array_buffer->GetBackingStore());
11861216
CHECK(target
11871217
->Set(env->context(),
11881218
FIXED_ONE_BYTE_STRING(env->isolate(), "zeroFill"),

src/node_http2.cc

+20-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace node {
1616

1717
using v8::ArrayBuffer;
1818
using v8::ArrayBufferView;
19+
using v8::BackingStore;
1920
using v8::Boolean;
2021
using v8::Context;
2122
using v8::Float64Array;
@@ -566,10 +567,18 @@ Http2Session::Http2Session(Environment* env,
566567

567568
{
568569
// Make the js_fields_ property accessible to JS land.
570+
std::unique_ptr<BackingStore> backing =
571+
ArrayBuffer::NewBackingStore(
572+
reinterpret_cast<uint8_t*>(&js_fields_),
573+
kSessionUint8FieldCount,
574+
[](void*, size_t, void*){},
575+
nullptr);
569576
Local<ArrayBuffer> ab =
570-
ArrayBuffer::New(env->isolate(),
571-
reinterpret_cast<uint8_t*>(&js_fields_),
572-
kSessionUint8FieldCount);
577+
ArrayBuffer::New(env->isolate(), std::move(backing));
578+
// TODO(thangktran): drop this check when V8 is pumped to 8.0 .
579+
if (!ab->IsExternal())
580+
ab->Externalize(ab->GetBackingStore());
581+
js_fields_ab_.Reset(env->isolate(), ab);
573582
Local<Uint8Array> uint8_arr =
574583
Uint8Array::New(ab, 0, kSessionUint8FieldCount);
575584
USE(wrap->Set(env->context(), env->fields_string(), uint8_arr));
@@ -581,6 +590,14 @@ Http2Session::~Http2Session() {
581590
Debug(this, "freeing nghttp2 session");
582591
nghttp2_session_del(session_);
583592
CHECK_EQ(current_nghttp2_memory_, 0);
593+
HandleScope handle_scope(env()->isolate());
594+
// Detach js_fields_ab_ to avoid having problem when new Http2Session
595+
// instances are created on the same location of previous
596+
// instances. This in turn will call ArrayBuffer::NewBackingStore()
597+
// multiple times with the same buffer address and causing error.
598+
// Ref: https://github.com/nodejs/node/pull/30782
599+
Local<ArrayBuffer> ab = js_fields_ab_.Get(env()->isolate());
600+
ab->Detach();
584601
}
585602

586603
std::string Http2Session::diagnostic_name() const {

src/node_http2.h

+1
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,7 @@ class Http2Session : public AsyncWrap,
989989

990990
// JS-accessible numeric fields, as indexed by SessionUint8Fields.
991991
SessionJSFields js_fields_ = {};
992+
v8::Global<v8::ArrayBuffer> js_fields_ab_;
992993

993994
// The session type: client or server
994995
nghttp2_session_type session_type_;

src/node_v8.cc

+40-9
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace node {
2828

2929
using v8::Array;
3030
using v8::ArrayBuffer;
31+
using v8::BackingStore;
3132
using v8::Context;
3233
using v8::FunctionCallbackInfo;
3334
using v8::HeapCodeStatistics;
@@ -162,12 +163,22 @@ void Initialize(Local<Object> target,
162163
const size_t heap_statistics_buffer_byte_length =
163164
sizeof(*env->heap_statistics_buffer()) * kHeapStatisticsPropertiesCount;
164165

166+
std::unique_ptr<BackingStore> heap_statistics_backing =
167+
ArrayBuffer::NewBackingStore(env->heap_statistics_buffer(),
168+
heap_statistics_buffer_byte_length,
169+
[](void*, size_t, void*){},
170+
nullptr);
171+
Local<ArrayBuffer> heap_statistics_ab =
172+
ArrayBuffer::New(env->isolate(),
173+
std::move(heap_statistics_backing));
174+
// TODO(thangktran): drop this check when V8 is pumped to 8.0 .
175+
if (!heap_statistics_ab->IsExternal())
176+
heap_statistics_ab->Externalize(
177+
heap_statistics_ab->GetBackingStore());
165178
target->Set(env->context(),
166179
FIXED_ONE_BYTE_STRING(env->isolate(),
167180
"heapStatisticsArrayBuffer"),
168-
ArrayBuffer::New(env->isolate(),
169-
env->heap_statistics_buffer(),
170-
heap_statistics_buffer_byte_length)).Check();
181+
heap_statistics_ab).Check();
171182

172183
#define V(i, _, name) \
173184
target->Set(env->context(), \
@@ -189,12 +200,22 @@ void Initialize(Local<Object> target,
189200
sizeof(*env->heap_code_statistics_buffer())
190201
* kHeapCodeStatisticsPropertiesCount;
191202

203+
std::unique_ptr<BackingStore> heap_code_statistics_backing =
204+
ArrayBuffer::NewBackingStore(env->heap_code_statistics_buffer(),
205+
heap_code_statistics_buffer_byte_length,
206+
[](void*, size_t, void*){},
207+
nullptr);
208+
Local<ArrayBuffer> heap_code_statistics_ab =
209+
ArrayBuffer::New(env->isolate(),
210+
std::move(heap_code_statistics_backing));
211+
// TODO(thangktran): drop this check when V8 is pumped to 8.0 .
212+
if (!heap_code_statistics_ab->IsExternal())
213+
heap_code_statistics_ab->Externalize(
214+
heap_code_statistics_ab->GetBackingStore());
192215
target->Set(env->context(),
193216
FIXED_ONE_BYTE_STRING(env->isolate(),
194217
"heapCodeStatisticsArrayBuffer"),
195-
ArrayBuffer::New(env->isolate(),
196-
env->heap_code_statistics_buffer(),
197-
heap_code_statistics_buffer_byte_length))
218+
heap_code_statistics_ab)
198219
.Check();
199220

200221
#define V(i, _, name) \
@@ -244,12 +265,22 @@ void Initialize(Local<Object> target,
244265
kHeapSpaceStatisticsPropertiesCount *
245266
number_of_heap_spaces;
246267

268+
std::unique_ptr<BackingStore> heap_space_statistics_backing =
269+
ArrayBuffer::NewBackingStore(env->heap_space_statistics_buffer(),
270+
heap_space_statistics_buffer_byte_length,
271+
[](void*, size_t, void*){},
272+
nullptr);
273+
Local<ArrayBuffer> heap_space_statistics_ab =
274+
ArrayBuffer::New(env->isolate(),
275+
std::move(heap_space_statistics_backing));
276+
// TODO(thangktran): drop this check when V8 is pumped to 8.0 .
277+
if (!heap_space_statistics_ab->IsExternal())
278+
heap_space_statistics_ab->Externalize(
279+
heap_space_statistics_ab->GetBackingStore());
247280
target->Set(env->context(),
248281
FIXED_ONE_BYTE_STRING(env->isolate(),
249282
"heapSpaceStatisticsArrayBuffer"),
250-
ArrayBuffer::New(env->isolate(),
251-
env->heap_space_statistics_buffer(),
252-
heap_space_statistics_buffer_byte_length))
283+
heap_space_statistics_ab)
253284
.Check();
254285

255286
#define V(i, _, name) \

src/node_worker.cc

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using node::kDisallowedInEnvironment;
2020
using v8::Array;
2121
using v8::ArrayBuffer;
22+
using v8::BackingStore;
2223
using v8::Boolean;
2324
using v8::Context;
2425
using v8::Float64Array;
@@ -622,6 +623,7 @@ void Worker::GetResourceLimits(const FunctionCallbackInfo<Value>& args) {
622623

623624
Local<Float64Array> Worker::GetResourceLimits(Isolate* isolate) const {
624625
Local<ArrayBuffer> ab = ArrayBuffer::New(isolate, sizeof(resource_limits_));
626+
625627
memcpy(ab->GetBackingStore()->Data(),
626628
resource_limits_,
627629
sizeof(resource_limits_));

test/addons/worker-buffer-callback/binding.cc

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ using v8::Object;
1010
using v8::Value;
1111

1212
uint32_t free_call_count = 0;
13-
char data[] = "hello";
1413

1514
void GetFreeCallCount(const FunctionCallbackInfo<Value>& args) {
1615
args.GetReturnValue().Set(free_call_count);
@@ -21,6 +20,9 @@ void Initialize(Local<Object> exports,
2120
Local<Context> context) {
2221
Isolate* isolate = context->GetIsolate();
2322
NODE_SET_METHOD(exports, "getFreeCallCount", GetFreeCallCount);
23+
24+
char* data = new char;
25+
2426
exports->Set(context,
2527
v8::String::NewFromUtf8(
2628
isolate, "buffer", v8::NewStringType::kNormal)
@@ -30,6 +32,7 @@ void Initialize(Local<Object> exports,
3032
data,
3133
sizeof(data),
3234
[](char* data, void* hint) {
35+
delete data;
3336
free_call_count++;
3437
},
3538
nullptr).ToLocalChecked()).Check();

test/js-native-api/test_typedarray/test_typedarray.c

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#define NAPI_EXPERIMENTAL
22
#include <js_native_api.h>
33
#include <string.h>
4+
#include <stdlib.h>
45
#include "../common.h"
56

67
static napi_value Multiply(napi_env env, napi_callback_info info) {
@@ -74,22 +75,33 @@ static napi_value Multiply(napi_env env, napi_callback_info info) {
7475
return output_array;
7576
}
7677

78+
static void FinalizeCallback(napi_env env,
79+
void* finalize_data,
80+
void* finalize_hint)
81+
{
82+
free(finalize_data);
83+
}
84+
7785
static napi_value External(napi_env env, napi_callback_info info) {
78-
static int8_t externalData[] = {0, 1, 2};
86+
const uint8_t nElem = 3;
87+
int8_t* externalData = malloc(nElem*sizeof(int8_t));
88+
externalData[0] = 0;
89+
externalData[1] = 1;
90+
externalData[2] = 2;
7991

8092
napi_value output_buffer;
8193
NAPI_CALL(env, napi_create_external_arraybuffer(
8294
env,
8395
externalData,
84-
sizeof(externalData),
85-
NULL, // finalize_callback
96+
nElem*sizeof(int8_t),
97+
FinalizeCallback,
8698
NULL, // finalize_hint
8799
&output_buffer));
88100

89101
napi_value output_array;
90102
NAPI_CALL(env, napi_create_typedarray(env,
91103
napi_int8_array,
92-
sizeof(externalData) / sizeof(int8_t),
104+
nElem,
93105
output_buffer,
94106
0,
95107
&output_array));

test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#include <stdio.h>
2+
#include <stdlib.h>
23
#include <node_api.h>
34
#include <assert.h>
45
#include "../../js-native-api/common.h"
56

67
uint32_t free_call_count = 0;
7-
char data[] = "hello";
88

99
napi_value GetFreeCallCount(napi_env env, napi_callback_info info) {
1010
napi_value value;
@@ -13,7 +13,7 @@ napi_value GetFreeCallCount(napi_env env, napi_callback_info info) {
1313
}
1414

1515
static void finalize_cb(napi_env env, void* finalize_data, void* hint) {
16-
assert(finalize_data == data);
16+
free(finalize_data);
1717
free_call_count++;
1818
}
1919

@@ -29,6 +29,9 @@ NAPI_MODULE_INIT() {
2929
// rather than a Node.js Buffer, since testing the latter would only test
3030
// the same code paths and not the ones specific to N-API.
3131
napi_value buffer;
32+
33+
char* data = malloc(sizeof(char));
34+
3235
NAPI_CALL(env, napi_create_external_arraybuffer(
3336
env,
3437
data,

0 commit comments

Comments
 (0)