Skip to content

Commit 397f433

Browse files
committed
[immutable-arraybuffer] ArrayBuffer.prototype.transferToImmutable
1 parent f807ed7 commit 397f433

File tree

9 files changed

+678
-0
lines changed

9 files changed

+678
-0
lines changed
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
// Copyright (C) 2025 Richard Gibson. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-get-arraybuffer.prototype.transfertoimmutable
6+
description: Throws a TypeError exception when `this` is a SharedArrayBuffer
7+
info: |
8+
ArrayBuffer.prototype.transferToImmutable ( [ newLength ] )
9+
1. Let O be the this value.
10+
2. Return ? ArrayBufferCopyAndDetach(O, newLength, ~immutable~).
11+
12+
ArrayBufferCopyAndDetach ( arrayBuffer, newLength, preserveResizability )
13+
1. Perform ? RequireInternalSlot(arrayBuffer, [[ArrayBufferData]]).
14+
2. If IsSharedArrayBuffer(arrayBuffer) is true, throw a TypeError exception.
15+
3. If newLength is undefined, then
16+
a. Let newByteLength be arrayBuffer.[[ArrayBufferByteLength]].
17+
4. Else,
18+
a. Let newByteLength be ? ToIndex(newLength).
19+
20+
ToIndex ( value )
21+
1. Let integer be ? ToIntegerOrInfinity(value).
22+
2. If integer is not in the inclusive interval from 0 to 2**53 - 1, throw a RangeError exception.
23+
3. Return integer.
24+
25+
ToIntegerOrInfinity ( argument )
26+
1. Let number be ? ToNumber(argument).
27+
2. If number is one of NaN, +0𝔽, or -0𝔽, return 0.
28+
3. If number is +∞𝔽, return +∞.
29+
4. If number is -∞𝔽, return -∞.
30+
5. Return truncate(ℝ(number)).
31+
features: [immutable-arraybuffer]
32+
includes: [compareArray.js]
33+
---*/
34+
35+
var ab = new ArrayBuffer(8);
36+
assert.sameValue(ab.transferToImmutable().byteLength, 8,
37+
"Must default to receiver byteLength.");
38+
ab = new ArrayBuffer(8);
39+
assert.sameValue(ab.transferToImmutable(undefined).byteLength, 8,
40+
"Must default undefined to receiver byteLength.");
41+
42+
function repr(value) {
43+
if (typeof value === "string") return JSON.stringify(value);
44+
if (typeof value === "bigint") return String(value) + "n";
45+
if (!value && 1/value === -Infinity) return "-0";
46+
return String(value);
47+
}
48+
49+
var goodLengths = [
50+
// Unmodified integral numbers
51+
[0, 0],
52+
[1, 1],
53+
[10, 10],
54+
// Truncated integral numbers
55+
[0.9, 0],
56+
[1.9, 1],
57+
[-0.9, 0],
58+
// Coerced to integral numbers
59+
[-0, 0],
60+
[null, 0],
61+
[false, 0],
62+
[true, 1],
63+
["", 0],
64+
["8", 8],
65+
["+9", 9],
66+
["10e0", 10],
67+
["+1.1E+1", 11],
68+
["+.12e2", 12],
69+
["130e-1", 13],
70+
["0b1110", 14],
71+
["0XF", 15],
72+
["0xf", 15],
73+
["0o20", 16],
74+
// Coerced to NaN and mapped to 0
75+
[NaN, 0],
76+
["7up", 0],
77+
["1_0", 0],
78+
["0x00_ff", 0]
79+
];
80+
81+
for (var i = 0; i < goodLengths.length; i++) {
82+
var rawLength = goodLengths[i][0];
83+
var intLength = goodLengths[i][1];
84+
var ab = new ArrayBuffer(8);
85+
assert.sameValue(ab.transferToImmutable(rawLength).byteLength, intLength,
86+
"transferToImmutable(" + repr(rawLength) + ")");
87+
}
88+
89+
var whitespace = "\t\v\f\uFEFF\u3000\n\r\u2028\u2029";
90+
for (var i = 0; i < goodLengths.length; i++) {
91+
var rawLength = goodLengths[i][0];
92+
var intLength = goodLengths[i][1];
93+
if (typeof rawLength === "number") {
94+
rawLength = repr(rawLength);
95+
} else if (typeof rawLength !== "string") {
96+
continue;
97+
}
98+
var paddedLength = whitespace + rawLength + whitespace;
99+
var ab = new ArrayBuffer(8);
100+
assert.sameValue(ab.transferToImmutable(paddedLength).byteLength, intLength,
101+
"transferToImmutable(" + repr(paddedLength) + ")");
102+
}
103+
104+
for (var i = 0; i < goodLengths.length; i++) {
105+
var rawLength = goodLengths[i][0];
106+
var intLength = goodLengths[i][1];
107+
var calls = [];
108+
var badValueOf = false;
109+
var badToString = false;
110+
var objLength = {
111+
valueOf() {
112+
calls.push("valueOf");
113+
return badValueOf ? {} : rawLength;
114+
},
115+
toString() {
116+
calls.push("toString");
117+
return badToString ? {} : rawLength;
118+
}
119+
};
120+
121+
var ab = new ArrayBuffer(8);
122+
assert.sameValue(ab.transferToImmutable(objLength).byteLength, intLength,
123+
"transferToImmutable({ valueOf: () => " + repr(rawLength) + " })");
124+
assert.compareArray(calls, ["valueOf"],
125+
"transferToImmutable({ valueOf: () => " + repr(rawLength) + " })");
126+
127+
badValueOf = true;
128+
calls = [];
129+
ab = new ArrayBuffer(8);
130+
assert.sameValue(ab.transferToImmutable(objLength).byteLength, intLength,
131+
"transferToImmutable({ toString: () => " + repr(rawLength) + " })");
132+
assert.compareArray(calls, ["valueOf", "toString"],
133+
"transferToImmutable({ toString: () => " + repr(rawLength) + " })");
134+
135+
badToString = true;
136+
if (typeof Symbol === undefined || !Symbol.toPrimitive) continue;
137+
calls = [];
138+
objLength[Symbol.toPrimitive] = function(hint) {
139+
calls.push("Symbol.toPrimitive(" + hint + ")");
140+
return rawLength;
141+
};
142+
ab = new ArrayBuffer(8);
143+
assert.sameValue(ab.transferToImmutable(objLength).byteLength, intLength,
144+
"transferToImmutable({ [Symbol.toPrimitive]: () => " + repr(rawLength) + " })");
145+
assert.compareArray(calls, ["Symbol.toPrimitive(number)"],
146+
"transferToImmutable({ [Symbol.toPrimitive]: () => " + repr(rawLength) + " })");
147+
}
148+
149+
var badLengths = [
150+
// Out of range numbers
151+
[-1, RangeError],
152+
[9007199254740992, RangeError], // Math.pow(2, 53) = 9007199254740992
153+
[Infinity, RangeError],
154+
[-Infinity, RangeError],
155+
// non-numbers
156+
typeof Symbol === undefined ? undefined : [Symbol("1"), TypeError],
157+
typeof Symbol === undefined || !Symbol.for ? undefined : [Symbol.for("1"), TypeError],
158+
typeof BigInt === undefined ? undefined : [BigInt(1), TypeError],
159+
];
160+
161+
for (var i = 0; i < badLengths.length; i++) {
162+
if (!badLengths[i]) continue;
163+
var rawLength = badLengths[i][0];
164+
var expectedErr = badLengths[i][1];
165+
var ab = new ArrayBuffer(8);
166+
assert.throws(expectedErr, function() {
167+
ab.transferToImmutable(rawLength);
168+
}, "transferToImmutable(" + repr(rawLength) + ")");
169+
}
170+
171+
for (var i = 0; i < badLengths.length; i++) {
172+
if (!badLengths[i]) continue;
173+
var rawLength = badLengths[i][0];
174+
var expectedErr = badLengths[i][1];
175+
if (typeof rawLength !== "number") continue;
176+
rawLength = repr(rawLength);
177+
var paddedLength = whitespace + rawLength + whitespace;
178+
var ab = new ArrayBuffer(8);
179+
assert.throws(expectedErr, function() {
180+
ab.transferToImmutable(paddedLength);
181+
}, "transferToImmutable(" + repr(paddedLength) + ")");
182+
}
183+
184+
for (var i = 0; i < badLengths.length; i++) {
185+
if (!badLengths[i]) continue;
186+
var rawLength = badLengths[i][0];
187+
var expectedErr = badLengths[i][1];
188+
var calls = [];
189+
var badValueOf = false;
190+
var badToString = false;
191+
var objLength = {
192+
valueOf() {
193+
calls.push("valueOf");
194+
return badValueOf ? {} : rawLength;
195+
},
196+
toString() {
197+
calls.push("toString");
198+
return badToString ? {} : rawLength;
199+
}
200+
};
201+
202+
var ab = new ArrayBuffer(8);
203+
assert.throws(expectedErr, function() {
204+
ab.transferToImmutable(objLength);
205+
}, "transferToImmutable({ valueOf: () => " + repr(rawLength) + " })");
206+
assert.compareArray(calls, ["valueOf"],
207+
"transferToImmutable({ valueOf: () => " + repr(rawLength) + " })");
208+
209+
badValueOf = true;
210+
calls = [];
211+
ab = new ArrayBuffer(8);
212+
assert.throws(expectedErr, function() {
213+
ab.transferToImmutable(objLength);
214+
}, "transferToImmutable({ toString: () => " + repr(rawLength) + " })");
215+
assert.compareArray(calls, ["valueOf", "toString"],
216+
"transferToImmutable({ toString: () => " + repr(rawLength) + " })");
217+
218+
badToString = true;
219+
if (typeof Symbol === undefined || !Symbol.toPrimitive) continue;
220+
calls = [];
221+
objLength[Symbol.toPrimitive] = function(hint) {
222+
calls.push("Symbol.toPrimitive(" + hint + ")");
223+
return rawLength;
224+
};
225+
ab = new ArrayBuffer(8);
226+
assert.throws(expectedErr, function() {
227+
ab.transferToImmutable(objLength);
228+
}, "transferToImmutable({ [Symbol.toPrimitive]: () => " + repr(rawLength) + " })");
229+
assert.compareArray(calls, ["Symbol.toPrimitive(number)"],
230+
"transferToImmutable({ [Symbol.toPrimitive]: () => " + repr(rawLength) + " })");
231+
}
232+
233+
var calls = [];
234+
var objLength = {
235+
toString() {
236+
calls.push("toString");
237+
return {};
238+
},
239+
valueOf() {
240+
calls.push("valueOf");
241+
return {};
242+
}
243+
};
244+
ab = new ArrayBuffer(8);
245+
assert.throws(TypeError, function() {
246+
ab.transferToImmutable(objLength);
247+
}, "transferToImmutable(badOrdinaryToPrimitive)");
248+
assert.compareArray(calls, ["valueOf", "toString"],
249+
"transferToImmutable(badOrdinaryToPrimitive)");
250+
if (typeof Symbol !== undefined && Symbol.toPrimitive) {
251+
calls = [];
252+
objLength[Symbol.toPrimitive] = function(hint) {
253+
calls.push("Symbol.toPrimitive(" + hint + ")");
254+
return {};
255+
};
256+
ab = new ArrayBuffer(8);
257+
assert.throws(TypeError, function() {
258+
ab.transferToImmutable(objLength);
259+
}, "transferToImmutable(badExoticToPrimitive)");
260+
}
261+
assert.compareArray(calls, ["Symbol.toPrimitive(number)"],
262+
"transferToImmutable(badExoticToPrimitive)");
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (C) 2025 Richard Gibson. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-arraybuffer.prototype.transfertoimmutable
6+
description: ArrayBuffer.prototype.transferToImmutable is not a constructor function
7+
info: |
8+
ECMAScript Standard Built-in Objects
9+
...
10+
Built-in function objects that are not identified as constructors do not
11+
implement the [[Construct]] internal method unless otherwise specified in the
12+
description of a particular function.
13+
features: [Reflect.construct, immutable-arraybuffer]
14+
includes: [isConstructor.js]
15+
---*/
16+
17+
assert(!isConstructor(ArrayBuffer.prototype.transferToImmutable),
18+
"ArrayBuffer.prototype.transferToImmutable is not a constructor");
19+
20+
var arrayBuffer = new ArrayBuffer(8);
21+
assert.throws(TypeError, function() {
22+
new arrayBuffer.transferToImmutable();
23+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (C) 2025 Richard Gibson. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-arraybuffer.prototype.transfertoimmutable
6+
description: Checks the "transferToImmutable" property of ArrayBuffer.prototype
7+
info: |
8+
ECMAScript Standard Built-in Objects
9+
...
10+
Every built-in function object, including constructors, has a "length"
11+
property whose value is a non-negative integral Number. Unless otherwise
12+
specified, this value is the number of required parameters shown in the
13+
subclause heading for the function description. Optional parameters and rest
14+
parameters are not included in the parameter count.
15+
...
16+
For functions that are specified as properties of objects, the name value is
17+
the property name string used to access the function.
18+
features: [immutable-arraybuffer]
19+
includes: [propertyHelper.js]
20+
---*/
21+
22+
verifyPrimordialCallableProperty(
23+
ArrayBuffer.prototype,
24+
"transferToImmutable",
25+
"transferToImmutable",
26+
0
27+
);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (C) 2025 Richard Gibson. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-get-arraybuffer.prototype.transfertoimmutable
6+
description: >
7+
Throws a TypeError exception when `this` does not have a [[ArrayBufferData]]
8+
internal slot
9+
info: |
10+
ArrayBuffer.prototype.transferToImmutable ( [ newLength ] )
11+
1. Let O be the this value.
12+
2. Return ? ArrayBufferCopyAndDetach(O, newLength, ~immutable~).
13+
14+
ArrayBufferCopyAndDetach ( arrayBuffer, newLength, preserveResizability )
15+
1. Perform ? RequireInternalSlot(arrayBuffer, [[ArrayBufferData]]).
16+
features: [DataView, Int8Array, ArrayBuffer, immutable-arraybuffer]
17+
includes: [compareArray.js]
18+
---*/
19+
20+
var fn = ArrayBuffer.prototype.transferToImmutable;
21+
assert.sameValue(typeof fn, "function", "Method must exist.");
22+
23+
var calls = [];
24+
var newLength = {
25+
valueOf() {
26+
calls.push("newLength.valueOf");
27+
return 1;
28+
}
29+
};
30+
31+
var badReceivers = [
32+
["plain object", {}],
33+
["array", []],
34+
["function", function(){}],
35+
["ArrayBuffer.prototype", ArrayBuffer.prototype],
36+
["TypedArray", new Int8Array(8)],
37+
["DataView", new DataView(new ArrayBuffer(8), 0)]
38+
];
39+
40+
for (var i = 0; i < badReceivers.length; i++) {
41+
var label = badReceivers[i][0];
42+
var value = badReceivers[i][1];
43+
calls = [];
44+
assert.throws(TypeError, function() {
45+
fn.call(value, newLength);
46+
}, label);
47+
assert.compareArray(calls, [],
48+
"[" + label + " receiver] Must verify internal slots before reading arguments.");
49+
}
50+
51+
calls = [];
52+
assert.throws(TypeError, function() {
53+
ArrayBuffer.prototype.transferToImmutable(newLength);
54+
}, "invoked as prototype method");
55+
assert.compareArray(calls, [],
56+
"[invoked as prototype method] Must verify internal slots before reading arguments.");
57+
58+
calls = [];
59+
assert.throws(TypeError, function() {
60+
fn(newLength);
61+
}, "invoked as function");
62+
assert.compareArray(calls, [],
63+
"[invoked as function] Must verify internal slots before reading arguments.");

0 commit comments

Comments
 (0)