summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/dataview
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/jit-test/tests/dataview
parentInitial commit. (diff)
downloadfirefox-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.js23
-rw-r--r--js/src/jit-test/tests/dataview/nan-canonicalization.js168
-rw-r--r--js/src/jit-test/tests/dataview/out-of-bounds-access.js61
-rw-r--r--js/src/jit-test/tests/dataview/read-aligned.js52
-rw-r--r--js/src/jit-test/tests/dataview/read-unaligned.js52
-rw-r--r--js/src/jit-test/tests/dataview/throws-on-detached.js43
-rw-r--r--js/src/jit-test/tests/dataview/write-aligned.js70
-rw-r--r--js/src/jit-test/tests/dataview/write-unaligned.js70
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);
+ }
+}