Skip to content

Commit b72f1d6

Browse files
tniessenGabriel Schulhof
authored andcommitted
Disable caching in ArrayBuffer
Caching the data pointer and the byteLength in the ArrayBuffer class causes it to behave incorrectly when the buffer is detached. PR-URL: #611 Reviewed-By: Nicola Del Gobbo <nicoladelgobbo@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent 0e7483e commit b72f1d6

File tree

4 files changed

+53
-31
lines changed

4 files changed

+53
-31
lines changed

napi-inl.h

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env, size_t byteLength) {
13461346
napi_status status = napi_create_arraybuffer(env, byteLength, &data, &value);
13471347
NAPI_THROW_IF_FAILED(env, status, ArrayBuffer());
13481348

1349-
return ArrayBuffer(env, value, data, byteLength);
1349+
return ArrayBuffer(env, value);
13501350
}
13511351

13521352
inline ArrayBuffer ArrayBuffer::New(napi_env env,
@@ -1357,7 +1357,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
13571357
env, externalData, byteLength, nullptr, nullptr, &value);
13581358
NAPI_THROW_IF_FAILED(env, status, ArrayBuffer());
13591359

1360-
return ArrayBuffer(env, value, externalData, byteLength);
1360+
return ArrayBuffer(env, value);
13611361
}
13621362

13631363
template <typename Finalizer>
@@ -1380,7 +1380,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
13801380
NAPI_THROW_IF_FAILED(env, status, ArrayBuffer());
13811381
}
13821382

1383-
return ArrayBuffer(env, value, externalData, byteLength);
1383+
return ArrayBuffer(env, value);
13841384
}
13851385

13861386
template <typename Finalizer, typename Hint>
@@ -1404,38 +1404,28 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
14041404
NAPI_THROW_IF_FAILED(env, status, ArrayBuffer());
14051405
}
14061406

1407-
return ArrayBuffer(env, value, externalData, byteLength);
1407+
return ArrayBuffer(env, value);
14081408
}
14091409

1410-
inline ArrayBuffer::ArrayBuffer() : Object(), _data(nullptr), _length(0) {
1410+
inline ArrayBuffer::ArrayBuffer() : Object() {
14111411
}
14121412

14131413
inline ArrayBuffer::ArrayBuffer(napi_env env, napi_value value)
1414-
: Object(env, value), _data(nullptr), _length(0) {
1415-
}
1416-
1417-
inline ArrayBuffer::ArrayBuffer(napi_env env, napi_value value, void* data, size_t length)
1418-
: Object(env, value), _data(data), _length(length) {
1414+
: Object(env, value) {
14191415
}
14201416

14211417
inline void* ArrayBuffer::Data() {
1422-
EnsureInfo();
1423-
return _data;
1418+
void* data;
1419+
napi_status status = napi_get_arraybuffer_info(_env, _value, &data, nullptr);
1420+
NAPI_THROW_IF_FAILED(_env, status, nullptr);
1421+
return data;
14241422
}
14251423

14261424
inline size_t ArrayBuffer::ByteLength() {
1427-
EnsureInfo();
1428-
return _length;
1429-
}
1430-
1431-
inline void ArrayBuffer::EnsureInfo() const {
1432-
// The ArrayBuffer instance may have been constructed from a napi_value whose
1433-
// length/data are not yet known. Fetch and cache these values just once,
1434-
// since they can never change during the lifetime of the ArrayBuffer.
1435-
if (_data == nullptr) {
1436-
napi_status status = napi_get_arraybuffer_info(_env, _value, &_data, &_length);
1437-
NAPI_THROW_IF_FAILED_VOID(_env, status);
1438-
}
1425+
size_t length;
1426+
napi_status status = napi_get_arraybuffer_info(_env, _value, nullptr, &length);
1427+
NAPI_THROW_IF_FAILED(_env, status, 0);
1428+
return length;
14391429
}
14401430

14411431
////////////////////////////////////////////////////////////////////////////////

napi.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ namespace Napi {
124124
class String;
125125
class Object;
126126
class Array;
127+
class ArrayBuffer;
127128
class Function;
128129
template <typename T> class Buffer;
129130
class Error;
@@ -806,13 +807,6 @@ namespace Napi {
806807

807808
void* Data(); ///< Gets a pointer to the data buffer.
808809
size_t ByteLength(); ///< Gets the length of the array buffer in bytes.
809-
810-
private:
811-
mutable void* _data;
812-
mutable size_t _length;
813-
814-
ArrayBuffer(napi_env env, napi_value value, void* data, size_t length);
815-
void EnsureInfo() const;
816810
};
817811

818812
/// A JavaScript typed-array value with unknown array type.

test/arraybuffer.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,37 @@ Value CheckEmptyBuffer(const CallbackInfo& info) {
151151
return Boolean::New(info.Env(), buffer.IsEmpty());
152152
}
153153

154+
void CheckDetachUpdatesData(const CallbackInfo& info) {
155+
if (!info[0].IsArrayBuffer()) {
156+
Error::New(info.Env(), "A buffer was expected.").ThrowAsJavaScriptException();
157+
return;
158+
}
159+
160+
if (!info[1].IsFunction()) {
161+
Error::New(info.Env(), "A function was expected.").ThrowAsJavaScriptException();
162+
return;
163+
}
164+
165+
ArrayBuffer buffer = info[0].As<ArrayBuffer>();
166+
Function detach = info[1].As<Function>();
167+
168+
// This potentially causes the buffer to cache its data pointer and length.
169+
buffer.Data();
170+
buffer.ByteLength();
171+
172+
detach.Call({});
173+
174+
if (buffer.Data() != nullptr) {
175+
Error::New(info.Env(), "Incorrect data pointer.").ThrowAsJavaScriptException();
176+
return;
177+
}
178+
179+
if (buffer.ByteLength() != 0) {
180+
Error::New(info.Env(), "Incorrect buffer length.").ThrowAsJavaScriptException();
181+
return;
182+
}
183+
}
184+
154185
} // end anonymous namespace
155186

156187
Object InitArrayBuffer(Env env) {
@@ -166,6 +197,7 @@ Object InitArrayBuffer(Env env) {
166197
exports["getFinalizeCount"] = Function::New(env, GetFinalizeCount);
167198
exports["createBufferWithConstructor"] = Function::New(env, CreateBufferWithConstructor);
168199
exports["checkEmptyBuffer"] = Function::New(env, CheckEmptyBuffer);
200+
exports["checkDetachUpdatesData"] = Function::New(env, CheckDetachUpdatesData);
169201

170202
return exports;
171203
}

test/arraybuffer.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,11 @@ function test(binding) {
6161
binding.arraybuffer.checkBuffer(test);
6262
assert.ok(test instanceof ArrayBuffer);
6363
},
64+
65+
'ArrayBuffer updates data pointer and length when detached',
66+
() => {
67+
const mem = new WebAssembly.Memory({ initial: 1 });
68+
binding.arraybuffer.checkDetachUpdatesData(mem.buffer, () => mem.grow(1));
69+
},
6470
]);
6571
}

0 commit comments

Comments
 (0)