diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/jit-test/tests/dataview | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/dataview')
-rw-r--r-- | js/src/jit-test/tests/dataview/create-out-of-bounds-ccw.js | 23 | ||||
-rw-r--r-- | js/src/jit-test/tests/dataview/nan-canonicalization.js | 168 | ||||
-rw-r--r-- | js/src/jit-test/tests/dataview/out-of-bounds-access.js | 61 | ||||
-rw-r--r-- | js/src/jit-test/tests/dataview/read-aligned.js | 52 | ||||
-rw-r--r-- | js/src/jit-test/tests/dataview/read-unaligned.js | 52 | ||||
-rw-r--r-- | js/src/jit-test/tests/dataview/throws-on-detached.js | 43 | ||||
-rw-r--r-- | js/src/jit-test/tests/dataview/write-aligned.js | 70 | ||||
-rw-r--r-- | js/src/jit-test/tests/dataview/write-unaligned.js | 70 |
8 files changed, 539 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/dataview/create-out-of-bounds-ccw.js b/js/src/jit-test/tests/dataview/create-out-of-bounds-ccw.js new file mode 100644 index 0000000000..11ca180064 --- /dev/null +++ b/js/src/jit-test/tests/dataview/create-out-of-bounds-ccw.js @@ -0,0 +1,23 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +// RangeError is from the correct global when resizable ArrayBuffer gets out-of-bounds. + +let g = newGlobal(); + +let rab = new g.ArrayBuffer(10, {maxByteLength: 10}); + +let newTarget = Object.defineProperty(function(){}.bind(), "prototype", { + get() { + rab.resize(0); + return DataView.prototype; + } +}); + +let err; +try { + Reflect.construct(DataView, [rab, 10], newTarget); +} catch (e) { + err = e; +} + +assertEq(err instanceof RangeError, true); diff --git a/js/src/jit-test/tests/dataview/nan-canonicalization.js b/js/src/jit-test/tests/dataview/nan-canonicalization.js new file mode 100644 index 0000000000..3e5d724cae --- /dev/null +++ b/js/src/jit-test/tests/dataview/nan-canonicalization.js @@ -0,0 +1,168 @@ +// Test NaN canonicalisation when reading from a DataView. + +load(libdir + "dataview.js"); + +// Float32 +function testF32() { + function writeBE(ui32, value) { + let ui8 = new Uint8Array(ui32.buffer); + + ui8[0] = (value >> 24) & 0xff; + ui8[1] = (value >> 16) & 0xff; + ui8[2] = (value >> 8) & 0xff; + ui8[3] = (value >> 0) & 0xff; + } + + function writeLE(ui32, value) { + let ui8 = new Uint8Array(ui32.buffer); + + ui8[0] = (value >> 0) & 0xff; + ui8[1] = (value >> 8) & 0xff; + ui8[2] = (value >> 16) & 0xff; + ui8[3] = (value >> 24) & 0xff; + } + + // Smallest and largest SNaNs and QNaNs, with and without sign-bit set. + const NaNs = [ + 0x7F80_0001, 0x7FBF_FFFF, 0x7FC0_0000, 0x7FFF_FFFF, + 0xFF80_0001, 0xFFBF_FFFF, 0xFFC0_0000, 0xFFFF_FFFF, + ]; + + const canonicalNaN = new Uint32Array(new Float32Array([NaN]).buffer)[0]; + + // Load from array so that Ion doesn't treat as constants. + const True = [true, 1]; + const False = [false, 0]; + + function f() { + let src_ui32 = new Uint32Array(1); + + let dst_f32 = new Float32Array(1); + let dst_ui32 = new Uint32Array(dst_f32.buffer); + + let dv = new DataView(src_ui32.buffer); + + for (let i = 0; i < 100; ++i) { + let nan = NaNs[i % NaNs.length]; + + // Write to typed array, implicitly using native endian. + src_ui32[0] = nan; + dst_f32[0] = dv.getFloat32(0, nativeIsLittleEndian); + assertEq(dst_ui32[0], canonicalNaN); + + // Write and read using big endian. |isLittleEndian| parameter is absent. + writeBE(src_ui32, nan); + dst_f32[0] = dv.getFloat32(0); + assertEq(dst_ui32[0], canonicalNaN); + + // Write and read using big endian. |isLittleEndian| parameter is a constant. + writeBE(src_ui32, nan); + dst_f32[0] = dv.getFloat32(0, false); + assertEq(dst_ui32[0], canonicalNaN); + + // Write and read using little endian. |isLittleEndian| parameter is a constant. + writeLE(src_ui32, nan); + dst_f32[0] = dv.getFloat32(0, true); + assertEq(dst_ui32[0], canonicalNaN); + + // Write and read using big endian. + writeBE(src_ui32, nan); + dst_f32[0] = dv.getFloat32(0, False[i & 1]); + assertEq(dst_ui32[0], canonicalNaN); + + // Write and read using little endian. + writeLE(src_ui32, nan); + dst_f32[0] = dv.getFloat32(0, True[i & 1]); + assertEq(dst_ui32[0], canonicalNaN); + } + } + + for (let i = 0; i < 2; ++i) f(); +} +testF32(); + +// Float64 +function testF64() { + function writeBE(ui64, value) { + let ui8 = new Uint8Array(ui64.buffer); + + ui8[0] = Number((value >> 56n) & 0xffn); + ui8[1] = Number((value >> 48n) & 0xffn); + ui8[2] = Number((value >> 40n) & 0xffn); + ui8[3] = Number((value >> 32n) & 0xffn); + ui8[4] = Number((value >> 24n) & 0xffn); + ui8[5] = Number((value >> 16n) & 0xffn); + ui8[6] = Number((value >> 8n) & 0xffn); + ui8[7] = Number((value >> 0n) & 0xffn); + } + + function writeLE(ui64, value) { + let ui8 = new Uint8Array(ui64.buffer); + + ui8[0] = Number((value >> 0n) & 0xffn); + ui8[1] = Number((value >> 8n) & 0xffn); + ui8[2] = Number((value >> 16n) & 0xffn); + ui8[3] = Number((value >> 24n) & 0xffn); + ui8[4] = Number((value >> 32n) & 0xffn); + ui8[5] = Number((value >> 40n) & 0xffn); + ui8[6] = Number((value >> 48n) & 0xffn); + ui8[7] = Number((value >> 56n) & 0xffn); + } + + // Smallest and largest SNaNs and QNaNs, with and without sign-bit set. + const NaNs = [ + 0x7FF0_0000_0000_0001n, 0x7FF7_FFFF_FFFF_FFFFn, 0x7FF8_0000_0000_0000n, 0x7FFF_FFFF_FFFF_FFFFn, + 0xFFF0_0000_0000_0001n, 0xFFF7_FFFF_FFFF_FFFFn, 0xFFF8_0000_0000_0000n, 0xFFFF_FFFF_FFFF_FFFFn, + ]; + + const canonicalNaN = new BigUint64Array(new Float64Array([NaN]).buffer)[0]; + + // Load from array so that Ion doesn't treat as constants. + const True = [true, 1]; + const False = [false, 0]; + + function f() { + let src_ui64 = new BigUint64Array(1); + + let dst_f64 = new Float64Array(1); + let dst_ui64 = new BigUint64Array(dst_f64.buffer); + + let dv = new DataView(src_ui64.buffer); + + for (let i = 0; i < 100; ++i) { + let nan = NaNs[i % NaNs.length]; + + src_ui64[0] = nan; + dst_f64[0] = dv.getFloat64(0, nativeIsLittleEndian); + assertEq(dst_ui64[0], canonicalNaN); + + // Write and read using big endian. |isLittleEndian| parameter is absent. + writeBE(src_ui64, nan); + dst_f64[0] = dv.getFloat64(0); + assertEq(dst_ui64[0], canonicalNaN); + + // Write and read using big endian. |isLittleEndian| parameter is a constant. + writeBE(src_ui64, nan); + dst_f64[0] = dv.getFloat64(0, false); + assertEq(dst_ui64[0], canonicalNaN); + + // Write and read using little endian. |isLittleEndian| parameter is a constant. + writeLE(src_ui64, nan); + dst_f64[0] = dv.getFloat64(0, true); + assertEq(dst_ui64[0], canonicalNaN); + + // Write and read using big endian. + writeBE(src_ui64, nan); + dst_f64[0] = dv.getFloat64(0, False[i & 1]); + assertEq(dst_ui64[0], canonicalNaN); + + // Write and read using little endian. + writeLE(src_ui64, nan); + dst_f64[0] = dv.getFloat64(0, True[i & 1]); + assertEq(dst_ui64[0], canonicalNaN); + } + } + + for (let i = 0; i < 2; ++i) f(); +} +testF64(); diff --git a/js/src/jit-test/tests/dataview/out-of-bounds-access.js b/js/src/jit-test/tests/dataview/out-of-bounds-access.js new file mode 100644 index 0000000000..53451228d0 --- /dev/null +++ b/js/src/jit-test/tests/dataview/out-of-bounds-access.js @@ -0,0 +1,61 @@ +// Out-of-bounds accesses are detected when inlining DataView. + +function testRead() { + const xs = [0x11_22_33_44, 0x55_66_77_88]; + + let dv = new DataView(new ArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2)); + dv.setInt32(0 * Int32Array.BYTES_PER_ELEMENT, xs[0], true); + dv.setInt32(1 * Int32Array.BYTES_PER_ELEMENT, xs[1], true); + + function f(dv, q) { + for (let i = 0; i <= 1000; ++i) { + // Perform an out-of-bounds read in the last iteration. + let k = (i & 1) * Int32Array.BYTES_PER_ELEMENT + (i === 1000 && q == 2 ? 7 : 0); + + let v = dv.getInt32(k, true); + assertEq(v, xs[i & 1]); + } + } + + try { + for (var i = 0; i <= 2; ++i) { + f(dv, i); + } + } catch (e) { + assertEq(e instanceof RangeError, true, e.message); + assertEq(i, 2); + } +} +testRead(); + +function testWrite() { + const xs = [0x11_22_33_44, 0x55_66_77_88]; + + let dv = new DataView(new ArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2)); + let ui8 = new Uint8Array(dv.buffer); + + function f(dv, q) { + for (let i = 0; i <= 1000; ++i) { + // Perform an out-of-bounds read in the last iteration. + let k = (i & 1) * Int32Array.BYTES_PER_ELEMENT + (i === 1000 && q == 2 ? 7 : 0); + let x = xs[i & 1]; + + dv.setInt32(k, x); + + assertEq(ui8[0 + (i & 1) * Int32Array.BYTES_PER_ELEMENT], (x >> 24) & 0xff); + assertEq(ui8[1 + (i & 1) * Int32Array.BYTES_PER_ELEMENT], (x >> 16) & 0xff); + assertEq(ui8[2 + (i & 1) * Int32Array.BYTES_PER_ELEMENT], (x >> 8) & 0xff); + assertEq(ui8[3 + (i & 1) * Int32Array.BYTES_PER_ELEMENT], (x >> 0) & 0xff); + } + } + + try { + for (var i = 0; i <= 2; ++i) { + f(dv, i); + } + } catch (e) { + assertEq(e instanceof RangeError, true, e.message); + assertEq(i, 2); + } +} +testWrite(); diff --git a/js/src/jit-test/tests/dataview/read-aligned.js b/js/src/jit-test/tests/dataview/read-aligned.js new file mode 100644 index 0000000000..005cc76dbe --- /dev/null +++ b/js/src/jit-test/tests/dataview/read-aligned.js @@ -0,0 +1,52 @@ +// Test aligned read access. + +load(libdir + "dataview.js"); + +// Create a new test function for each scalar type. +function createRead(data) { + const name = typeName(data.type); + const offset = 0; + + return Function("data", ` + const {values, littleEndian, bigEndian} = data; + + // Load from array so that Ion doesn't treat as constants. + const True = [true, 1]; + const False = [false, 0]; + + const ab = new ArrayBuffer(${data.values.length * data.type.BYTES_PER_ELEMENT + offset}); + const dv = new DataView(ab); + + new ${data.type.name}(ab, 0, ${data.values.length}).set(values); + + new Uint8Array(ab).copyWithin(${offset}, 0); + + for (let i = 0; i < 100; ++i) { + let j = i % values.length; + let index = j * ${data.type.BYTES_PER_ELEMENT} + ${offset}; + + let v1 = dv.get${name}(index); + assertEq(v1, bigEndian[j]); + + let v2 = dv.get${name}(index, true); + assertEq(v2, littleEndian[j]); + + let v3 = dv.get${name}(index, false); + assertEq(v3, bigEndian[j]); + + let v4 = dv.get${name}(index, True[i & 1]); + assertEq(v4, littleEndian[j]); + + let v5 = dv.get${name}(index, False[i & 1]); + assertEq(v5, bigEndian[j]); + } + `); +} + +for (let data of createTestData()) { + let f = createRead(data); + + for (let i = 0; i < 2; ++i) { + f(data); + } +} diff --git a/js/src/jit-test/tests/dataview/read-unaligned.js b/js/src/jit-test/tests/dataview/read-unaligned.js new file mode 100644 index 0000000000..2e653ba8c4 --- /dev/null +++ b/js/src/jit-test/tests/dataview/read-unaligned.js @@ -0,0 +1,52 @@ +// Test unaligned read access. + +load(libdir + "dataview.js"); + +// Create a new test function for each scalar type. +function createRead(data) { + const name = typeName(data.type); + const offset = 1; + + return Function("data", ` + const {values, littleEndian, bigEndian} = data; + + // Load from array so that Ion doesn't treat as constants. + const True = [true, 1]; + const False = [false, 0]; + + const ab = new ArrayBuffer(${data.values.length * data.type.BYTES_PER_ELEMENT + offset}); + const dv = new DataView(ab); + + new ${data.type.name}(ab, 0, ${data.values.length}).set(values); + + new Uint8Array(ab).copyWithin(${offset}, 0); + + for (let i = 0; i < 100; ++i) { + let j = i % values.length; + let index = j * ${data.type.BYTES_PER_ELEMENT} + ${offset}; + + let v1 = dv.get${name}(index); + assertEq(v1, bigEndian[j]); + + let v2 = dv.get${name}(index, true); + assertEq(v2, littleEndian[j]); + + let v3 = dv.get${name}(index, false); + assertEq(v3, bigEndian[j]); + + let v4 = dv.get${name}(index, True[i & 1]); + assertEq(v4, littleEndian[j]); + + let v5 = dv.get${name}(index, False[i & 1]); + assertEq(v5, bigEndian[j]); + } + `); +} + +for (let data of createTestData()) { + let f = createRead(data); + + for (let i = 0; i < 2; ++i) { + f(data); + } +} diff --git a/js/src/jit-test/tests/dataview/throws-on-detached.js b/js/src/jit-test/tests/dataview/throws-on-detached.js new file mode 100644 index 0000000000..fb06a3c34c --- /dev/null +++ b/js/src/jit-test/tests/dataview/throws-on-detached.js @@ -0,0 +1,43 @@ +// TypeError is thrown when the underlying ArrayBuffer is detached. + +function testByteOffset() { + var ab = new ArrayBuffer(10); + var dv = new DataView(ab, 4, 0); + + var q = 0; + var error; + try { + for (var i = 0; i <= 200; ++i) { + if (i === 200) { + detachArrayBuffer(ab); + } + q += dv.byteOffset; + } + } catch (e) { + error = e; + } + assertEq(q, 4 * 200); + assertEq(error instanceof TypeError, true); +} +testByteOffset(); + +function testByteLength() { + var ab = new ArrayBuffer(10); + var dv = new DataView(ab, 4, 6); + + var q = 0; + var error; + try { + for (var i = 0; i <= 200; ++i) { + if (i === 200) { + detachArrayBuffer(ab); + } + q += dv.byteLength; + } + } catch (e) { + error = e; + } + assertEq(q, 6 * 200); + assertEq(error instanceof TypeError, true); +} +testByteLength(); diff --git a/js/src/jit-test/tests/dataview/write-aligned.js b/js/src/jit-test/tests/dataview/write-aligned.js new file mode 100644 index 0000000000..f7d014780f --- /dev/null +++ b/js/src/jit-test/tests/dataview/write-aligned.js @@ -0,0 +1,70 @@ +// Test aligned write access. + +load(libdir + "dataview.js"); + +// Create a new test function for each scalar type. +function createWrite(data) { + const name = typeName(data.type); + const offset = 0; + + return Function("data", ` + const {values, littleEndian, bigEndian} = data; + + // Load from array so that Ion doesn't treat as constants. + const True = [true, 1]; + const False = [false, 0]; + + const src = new ${data.type.name}(values); + + const ab = new ArrayBuffer(${data.type.BYTES_PER_ELEMENT + offset}); + const dv = new DataView(ab); + + const srcUint8 = new Uint8Array(src.buffer); + const dstUint8 = new Uint8Array(ab); + + function assertSameContents(idx, msg) { + for (let i = 0; i < ${data.type.BYTES_PER_ELEMENT}; ++i) { + assertEq(dstUint8[i + ${offset}], srcUint8[i + idx * ${data.type.BYTES_PER_ELEMENT}]); + } + } + + for (let i = 0; i < 100; ++i) { + let j = i % values.length; + + // Skip over NaNs to avoid false-negatives due to NaN canonicalisation. + if (${name === "Float32" || name === "Float64"}) { + if (Number.isNaN(bigEndian[j]) || Number.isNaN(littleEndian[j])) { + continue; + } + } + + dstUint8.fill(0); + dv.set${name}(${offset}, bigEndian[j]); + assertSameContents(j, "default"); + + dstUint8.fill(0); + dv.set${name}(${offset}, littleEndian[j], true); + assertSameContents(j, "little"); + + dstUint8.fill(0); + dv.set${name}(${offset}, bigEndian[j], false); + assertSameContents(j, "big"); + + dstUint8.fill(0); + dv.set${name}(${offset}, littleEndian[j], True[i & 1]); + assertSameContents(j, "little, dynamic"); + + dstUint8.fill(0); + dv.set${name}(${offset}, bigEndian[j], False[i & 1]); + assertSameContents(j, "big, dynamic"); + } + `); +} + +for (let data of createTestData()) { + let f = createWrite(data); + + for (let i = 0; i < 2; ++i) { + f(data); + } +} diff --git a/js/src/jit-test/tests/dataview/write-unaligned.js b/js/src/jit-test/tests/dataview/write-unaligned.js new file mode 100644 index 0000000000..e8d0b262f4 --- /dev/null +++ b/js/src/jit-test/tests/dataview/write-unaligned.js @@ -0,0 +1,70 @@ +// Test unaligned write access. + +load(libdir + "dataview.js"); + +// Create a new test function for each scalar type. +function createWrite(data) { + const name = typeName(data.type); + const offset = 1; + + return Function("data", ` + const {values, littleEndian, bigEndian} = data; + + // Load from array so that Ion doesn't treat as constants. + const True = [true, 1]; + const False = [false, 0]; + + const src = new ${data.type.name}(values); + + const ab = new ArrayBuffer(${data.type.BYTES_PER_ELEMENT + offset}); + const dv = new DataView(ab); + + const srcUint8 = new Uint8Array(src.buffer); + const dstUint8 = new Uint8Array(ab); + + function assertSameContents(idx, msg) { + for (let i = 0; i < ${data.type.BYTES_PER_ELEMENT}; ++i) { + assertEq(dstUint8[i + ${offset}], srcUint8[i + idx * ${data.type.BYTES_PER_ELEMENT}]); + } + } + + for (let i = 0; i < 100; ++i) { + let j = i % values.length; + + // Skip over NaNs to avoid false-negatives due to NaN canonicalisation. + if (${name === "Float32" || name === "Float64"}) { + if (Number.isNaN(bigEndian[j]) || Number.isNaN(littleEndian[j])) { + continue; + } + } + + dstUint8.fill(0); + dv.set${name}(${offset}, bigEndian[j]); + assertSameContents(j, "default"); + + dstUint8.fill(0); + dv.set${name}(${offset}, littleEndian[j], true); + assertSameContents(j, "little"); + + dstUint8.fill(0); + dv.set${name}(${offset}, bigEndian[j], false); + assertSameContents(j, "big"); + + dstUint8.fill(0); + dv.set${name}(${offset}, littleEndian[j], True[i & 1]); + assertSameContents(j, "little, dynamic"); + + dstUint8.fill(0); + dv.set${name}(${offset}, bigEndian[j], False[i & 1]); + assertSameContents(j, "big, dynamic"); + } + `); +} + +for (let data of createTestData()) { + let f = createWrite(data); + + for (let i = 0; i < 2; ++i) { + f(data); + } +} |