lib: short-circuit WebIDL BufferSource SAB check · nodejs/node@37d3913
1+// Flags: --expose-internals
2+'use strict';
3+4+require('../common');
5+const assert = require('assert');
6+const { test } = require('node:test');
7+8+const { converters } = require('internal/webidl');
9+10+const TYPED_ARRAY_CTORS = [
11+Uint8Array, Int8Array, Uint8ClampedArray,
12+Uint16Array, Int16Array,
13+Uint32Array, Int32Array,
14+Float16Array, Float32Array, Float64Array,
15+BigInt64Array, BigUint64Array,
16+];
17+18+test('BufferSource accepts ArrayBuffer', () => {
19+const ab = new ArrayBuffer(8);
20+assert.strictEqual(converters.BufferSource(ab), ab);
21+});
22+23+test('BufferSource accepts all TypedArray kinds', () => {
24+for (const Ctor of TYPED_ARRAY_CTORS) {
25+const ta = new Ctor(4);
26+assert.strictEqual(converters.BufferSource(ta), ta);
27+}
28+});
29+30+test('BufferSource accepts Buffer', () => {
31+const buf = Buffer.alloc(8);
32+assert.strictEqual(converters.BufferSource(buf), buf);
33+});
34+35+test('BufferSource accepts DataView', () => {
36+const dv = new DataView(new ArrayBuffer(8));
37+assert.strictEqual(converters.BufferSource(dv), dv);
38+});
39+40+test('BufferSource accepts ArrayBuffer subclass instance', () => {
41+class MyAB extends ArrayBuffer {}
42+const sub = new MyAB(8);
43+assert.strictEqual(converters.BufferSource(sub), sub);
44+});
45+46+test('BufferSource accepts TypedArray with null prototype', () => {
47+const ta = new Uint8Array(4);
48+Object.setPrototypeOf(ta, null);
49+assert.strictEqual(converters.BufferSource(ta), ta);
50+});
51+52+test('BufferSource accepts DataView with null prototype', () => {
53+const dv = new DataView(new ArrayBuffer(4));
54+Object.setPrototypeOf(dv, null);
55+assert.strictEqual(converters.BufferSource(dv), dv);
56+});
57+58+test('BufferSource accepts ArrayBuffer with null prototype', () => {
59+const ab = new ArrayBuffer(4);
60+Object.setPrototypeOf(ab, null);
61+assert.strictEqual(converters.BufferSource(ab), ab);
62+});
63+64+test('BufferSource rejects SharedArrayBuffer', () => {
65+assert.throws(
66+() => converters.BufferSource(new SharedArrayBuffer(4)),
67+{ code: 'ERR_INVALID_ARG_TYPE' },
68+);
69+});
70+71+test('BufferSource rejects SAB-backed TypedArray', () => {
72+const view = new Uint8Array(new SharedArrayBuffer(4));
73+assert.throws(
74+() => converters.BufferSource(view),
75+{ code: 'ERR_INVALID_ARG_TYPE' },
76+);
77+});
78+79+test('BufferSource rejects SAB-backed DataView', () => {
80+const dv = new DataView(new SharedArrayBuffer(4));
81+assert.throws(
82+() => converters.BufferSource(dv),
83+{ code: 'ERR_INVALID_ARG_TYPE' },
84+);
85+});
86+87+test('BufferSource rejects SAB view whose buffer prototype was reassigned', () => {
88+const sab = new SharedArrayBuffer(4);
89+Object.setPrototypeOf(sab, ArrayBuffer.prototype);
90+const view = new Uint8Array(sab);
91+assert.throws(
92+() => converters.BufferSource(view),
93+{ code: 'ERR_INVALID_ARG_TYPE' },
94+);
95+});
96+97+test('BufferSource accepts a detached ArrayBuffer', () => {
98+const ab = new ArrayBuffer(8);
99+structuredClone(ab, { transfer: [ab] });
100+assert.strictEqual(ab.byteLength, 0);
101+assert.strictEqual(converters.BufferSource(ab), ab);
102+});
103+104+test('BufferSource rejects objects with a forged @@toStringTag', () => {
105+const fake = { [Symbol.toStringTag]: 'Uint8Array' };
106+assert.throws(
107+() => converters.BufferSource(fake),
108+{ code: 'ERR_INVALID_ARG_TYPE' },
109+);
110+});
111+112+for (const value of [null, undefined, 0, 1, 1n, '', 'x', true, Symbol('s'), [],
113+{}, () => {}]) {
114+test(`BufferSource rejects ${typeof value} ${String(value)}`, () => {
115+assert.throws(
116+() => converters.BufferSource(value),
117+{ code: 'ERR_INVALID_ARG_TYPE' },
118+);
119+});
120+}
121+122+test('ArrayBufferView accepts all TypedArray kinds', () => {
123+for (const Ctor of TYPED_ARRAY_CTORS) {
124+const ta = new Ctor(4);
125+assert.strictEqual(converters.ArrayBufferView(ta), ta);
126+}
127+});
128+129+test('ArrayBufferView accepts DataView', () => {
130+const dv = new DataView(new ArrayBuffer(8));
131+assert.strictEqual(converters.ArrayBufferView(dv), dv);
132+});
133+134+test('ArrayBufferView accepts TypedArray subclass instance', () => {
135+class MyU8 extends Uint8Array {}
136+const sub = new MyU8(4);
137+assert.strictEqual(converters.ArrayBufferView(sub), sub);
138+});
139+140+test('ArrayBufferView accepts TypedArray with null prototype', () => {
141+const ta = new Uint8Array(4);
142+Object.setPrototypeOf(ta, null);
143+assert.strictEqual(converters.ArrayBufferView(ta), ta);
144+});
145+146+test('ArrayBufferView accepts DataView with null prototype', () => {
147+const dv = new DataView(new ArrayBuffer(4));
148+Object.setPrototypeOf(dv, null);
149+assert.strictEqual(converters.ArrayBufferView(dv), dv);
150+});
151+152+test('ArrayBufferView rejects raw ArrayBuffer', () => {
153+assert.throws(
154+() => converters.ArrayBufferView(new ArrayBuffer(4)),
155+{ code: 'ERR_INVALID_ARG_TYPE' },
156+);
157+});
158+159+test('ArrayBufferView rejects raw SharedArrayBuffer', () => {
160+assert.throws(
161+() => converters.ArrayBufferView(new SharedArrayBuffer(4)),
162+{ code: 'ERR_INVALID_ARG_TYPE' },
163+);
164+});
165+166+test('ArrayBufferView rejects SAB-backed TypedArray', () => {
167+const view = new Uint8Array(new SharedArrayBuffer(4));
168+assert.throws(
169+() => converters.ArrayBufferView(view),
170+{ code: 'ERR_INVALID_ARG_TYPE' },
171+);
172+});
173+174+test('ArrayBufferView rejects SAB-backed DataView', () => {
175+const dv = new DataView(new SharedArrayBuffer(4));
176+assert.throws(
177+() => converters.ArrayBufferView(dv),
178+{ code: 'ERR_INVALID_ARG_TYPE' },
179+);
180+});
181+182+test('ArrayBufferView rejects SAB view whose buffer prototype was reassigned', () => {
183+const sab = new SharedArrayBuffer(4);
184+Object.setPrototypeOf(sab, ArrayBuffer.prototype);
185+const view = new Uint8Array(sab);
186+assert.throws(
187+() => converters.ArrayBufferView(view),
188+{ code: 'ERR_INVALID_ARG_TYPE' },
189+);
190+});
191+192+test('ArrayBufferView rejects objects with a forged @@toStringTag', () => {
193+const fake = { [Symbol.toStringTag]: 'Uint8Array' };
194+assert.throws(
195+() => converters.ArrayBufferView(fake),
196+{ code: 'ERR_INVALID_ARG_TYPE' },
197+);
198+});
199+200+for (const value of [null, undefined, 0, 1, 1n, '', 'x', true, Symbol('s'), [],
201+{}, () => {}]) {
202+test(`ArrayBufferView rejects ${typeof value} ${String(value)}`, () => {
203+assert.throws(
204+() => converters.ArrayBufferView(value),
205+{ code: 'ERR_INVALID_ARG_TYPE' },
206+);
207+});
208+}