Skip to content

Commit 82d44f4

Browse files
committed
util: add isArrayBufferDetached method
1 parent c74dbd2 commit 82d44f4

File tree

7 files changed

+53
-20
lines changed

7 files changed

+53
-20
lines changed

Diff for: doc/api/util.md

+21
Original file line numberDiff line numberDiff line change
@@ -2024,6 +2024,27 @@ util.types.isAnyArrayBuffer(new ArrayBuffer()); // Returns true
20242024
util.types.isAnyArrayBuffer(new SharedArrayBuffer()); // Returns true
20252025
```
20262026
2027+
### `util.types.isArrayBufferDetached(value)`
2028+
2029+
<!-- YAML
2030+
added: REPLACEME
2031+
-->
2032+
2033+
* `value` {any}
2034+
* Returns: {boolean}
2035+
2036+
Returns `true` if the value is a built-in [`ArrayBuffer`][] and
2037+
is detached. Detached arrays have a byte length of 0, which prevents
2038+
JavaScript from ever accessing underlying backing store.
2039+
2040+
For example, we can end up with a detached buffer when using a BYOB
2041+
(bring your own buffer) on a ReadableStream.
2042+
2043+
```js
2044+
util.types.isArrayBufferDetached(null); // Returns false
2045+
util.types.isArrayBufferDetached(new ArrayBuffer()); // Returns false
2046+
```
2047+
20272048
### `util.types.isArrayBufferView(value)`
20282049
20292050
<!-- YAML

Diff for: lib/internal/util/types.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
const {
44
ArrayBufferIsView,
5+
ArrayBufferPrototypeGetByteLength,
56
ObjectDefineProperties,
67
TypedArrayPrototypeGetSymbolToStringTag,
78
} = primordials;
89

10+
const { isArrayBufferDetached: _isArrayBufferDetached, ...internalTypes } = internalBinding('types');
11+
912
function isTypedArray(value) {
1013
return TypedArrayPrototypeGetSymbolToStringTag(value) !== undefined;
1114
}
@@ -54,8 +57,16 @@ function isBigUint64Array(value) {
5457
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'BigUint64Array';
5558
}
5659

60+
function isArrayBufferDetached(value) {
61+
if (ArrayBufferPrototypeGetByteLength(value) === 0) {
62+
return _isArrayBufferDetached(value);
63+
}
64+
return false;
65+
}
66+
5767
module.exports = {
58-
...internalBinding('types'),
68+
...internalTypes,
69+
isArrayBufferDetached,
5970
isArrayBufferView: ArrayBufferIsView,
6071
isTypedArray,
6172
isUint8Array,

Diff for: lib/internal/webstreams/readablestream.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const {
4343
} = internalBinding('messaging');
4444

4545
const {
46+
isArrayBufferDetached,
4647
isArrayBufferView,
4748
isDataView,
4849
} = require('internal/util/types');
@@ -104,7 +105,6 @@ const {
104105
extractHighWaterMark,
105106
extractSizeAlgorithm,
106107
lazyTransfer,
107-
isDetachedBuffer,
108108
isViewedArrayBufferDetached,
109109
isBrandCheck,
110110
resetQueue,
@@ -669,7 +669,7 @@ class ReadableStreamBYOBRequest {
669669
const viewBuffer = ArrayBufferViewGetBuffer(view);
670670
const viewBufferByteLength = ArrayBufferGetByteLength(viewBuffer);
671671

672-
if (isDetachedBuffer(viewBuffer)) {
672+
if (isArrayBufferDetached(viewBuffer)) {
673673
throw new ERR_INVALID_STATE.TypeError('Viewed ArrayBuffer is detached');
674674
}
675675

@@ -2643,7 +2643,7 @@ function readableByteStreamControllerEnqueue(controller, chunk) {
26432643
if (pendingPullIntos.length) {
26442644
const firstPendingPullInto = pendingPullIntos[0];
26452645

2646-
if (isDetachedBuffer(firstPendingPullInto.buffer)) {
2646+
if (isArrayBufferDetached(firstPendingPullInto.buffer)) {
26472647
throw new ERR_INVALID_STATE.TypeError(
26482648
'Destination ArrayBuffer is detached',
26492649
);

Diff for: lib/internal/webstreams/util.js

+2-15
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const {
3232
} = internalBinding('buffer');
3333

3434
const {
35+
isArrayBufferDetached,
3536
isPromise,
3637
} = require('internal/util/types');
3738

@@ -139,23 +140,10 @@ function transferArrayBuffer(buffer) {
139140
return res;
140141
}
141142

142-
function isDetachedBuffer(buffer) {
143-
if (ArrayBufferGetByteLength(buffer) === 0) {
144-
// TODO(daeyeon): Consider using C++ builtin to improve performance.
145-
try {
146-
new Uint8Array(buffer);
147-
} catch (error) {
148-
assert(error.name === 'TypeError');
149-
return true;
150-
}
151-
}
152-
return false;
153-
}
154-
155143
function isViewedArrayBufferDetached(view) {
156144
return (
157145
ArrayBufferViewGetByteLength(view) === 0 &&
158-
isDetachedBuffer(ArrayBufferViewGetBuffer(view))
146+
isArrayBufferDetached(ArrayBufferViewGetBuffer(view))
159147
);
160148
}
161149

@@ -256,7 +244,6 @@ module.exports = {
256244
extractSizeAlgorithm,
257245
lazyTransfer,
258246
isBrandCheck,
259-
isDetachedBuffer,
260247
isPromisePending,
261248
isViewedArrayBufferDetached,
262249
peekQueueValue,

Diff for: src/node_types.cc

+13
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ static void IsBoxedPrimitive(const FunctionCallbackInfo<Value>& args) {
6161
args[0]->IsSymbolObject());
6262
}
6363

64+
static void IsArrayBufferDetached(const FunctionCallbackInfo<Value>& args) {
65+
if (args[0]->IsArrayBuffer()) {
66+
auto buffer = args[0].As<v8::ArrayBuffer>();
67+
args.GetReturnValue().Set(buffer->WasDetached());
68+
return;
69+
}
70+
71+
args.GetReturnValue().Set(false);
72+
}
73+
6474
void InitializeTypes(Local<Object> target,
6575
Local<Value> unused,
6676
Local<Context> context,
@@ -71,6 +81,8 @@ void InitializeTypes(Local<Object> target,
7181

7282
SetMethodNoSideEffect(context, target, "isAnyArrayBuffer", IsAnyArrayBuffer);
7383
SetMethodNoSideEffect(context, target, "isBoxedPrimitive", IsBoxedPrimitive);
84+
SetMethodNoSideEffect(
85+
context, target, "isArrayBufferDetached", IsArrayBufferDetached);
7486
}
7587

7688
} // anonymous namespace
@@ -82,6 +94,7 @@ void RegisterTypesExternalReferences(ExternalReferenceRegistry* registry) {
8294

8395
registry->Register(IsAnyArrayBuffer);
8496
registry->Register(IsBoxedPrimitive);
97+
registry->Register(IsArrayBufferDetached);
8598
}
8699
} // namespace node
87100

Diff for: test/parallel/test-util-types.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ for (const [ value, _method ] of [
5656
for (const key of Object.keys(types)) {
5757
if ((types.isArrayBufferView(value) ||
5858
types.isAnyArrayBuffer(value)) && key.includes('Array') ||
59-
key === 'isBoxedPrimitive') {
59+
key === 'isBoxedPrimitive' || key === 'isArrayBufferDetached') {
6060
continue;
6161
}
6262

Diff for: typings/internalBinding/types.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ declare function InternalBinding(binding: 'types'): {
55
isArrayBuffer(value: unknown): value is ArrayBuffer;
66
isArgumentsObject(value: unknown): value is ArrayLike<unknown>;
77
isBoxedPrimitive(value: unknown): value is (BigInt | Boolean | Number | String | Symbol);
8+
isArrayBufferDetached(value: unknown): boolean;
89
isDataView(value: unknown): value is DataView;
910
isExternal(value: unknown): value is Object;
1011
isMap(value: unknown): value is Map<unknown, unknown>;

0 commit comments

Comments
 (0)