|
| 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)"); |
0 commit comments