forked from WebKit/WebKit
-
Notifications
You must be signed in to change notification settings - Fork 52
Add DFG/FTL nodes for byte-offset scalar accessors on ArrayBufferView receivers #330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Jarred-Sumner
wants to merge
12
commits into
main
Choose a base branch
from
claude/buffer-jit
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 9 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
fb957cf
Add DFG/FTL nodes for byte-offset scalar accessors on ArrayBufferView…
Jarred-Sumner abf1dc3
Fixup: keep BufferReadInt/BufferWrite edges well-formed on the ForceE…
Jarred-Sumner 4fa7b25
Address review: parser resizable fallback, FTL guards, test assertion
Jarred-Sumner f2c0e3b
Buffer accessors: variable-width descriptors, inlined when the byteLe…
Jarred-Sumner be77ad7
JSDollarVM: mark the buffer accessor test host functions JSC_HOST_CAL…
Jarred-Sumner 2a16ffb
Merge remote-tracking branch 'origin/main' into claude/buffer-jit
Jarred-Sumner 6d8df12
Buffer accessors: graph-level value range checks, dead return value
Jarred-Sumner a4ecdd6
JSDollarVM: purify NaN on buffer accessor float reads, reject NaN writes
Jarred-Sumner cee81fb
Remove the buffer accessor comments
Jarred-Sumner b2e9f59
Buffer accessors: use the Int52 typed array length in the FTL
Jarred-Sumner 93cf9dc
Buffer accessors: stress test views with 2GB / ~4GB byteOffsets
Jarred-Sumner 8debd97
Buffer accessors: differential fuzzer stress test, restore Overflow gate
Jarred-Sumner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| //@ requireOptions("--useDollarVM=1") | ||
|
|
||
| function shouldBe(actual, expected, message) { | ||
| if (actual !== expected) throw new Error(message + ": expected " + expected + " but got " + actual); | ||
| } | ||
| function shouldThrow(f, expected, message) { | ||
| let error = null; | ||
| try { | ||
| f(); | ||
| } catch (e) { | ||
| error = e; | ||
| } | ||
| if (!(error instanceof expected)) throw new Error(message + ": expected a " + expected.name + " but got " + error); | ||
| } | ||
|
|
||
| const accessors = $vm.createBufferAccessors(); | ||
| class Buffer extends Uint8Array {} | ||
| Object.assign(Buffer.prototype, accessors); | ||
|
|
||
| const buf = new Buffer(64); | ||
| const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); | ||
|
|
||
| function writeBigInt64LE(b, v, o) { | ||
| return b.writeBigInt64LE(v, o); | ||
| } | ||
| noInline(writeBigInt64LE); | ||
| function writeBigInt64BE(b, v, o) { | ||
| return b.writeBigInt64BE(v, o); | ||
| } | ||
| noInline(writeBigInt64BE); | ||
| function writeBigUInt64LE(b, v, o) { | ||
| return b.writeBigUInt64LE(v, o); | ||
| } | ||
| noInline(writeBigUInt64LE); | ||
| function writeBigUInt64BE(b, v, o) { | ||
| return b.writeBigUInt64BE(v, o); | ||
| } | ||
| noInline(writeBigUInt64BE); | ||
|
|
||
| const values = [0n, 1n, -1n, 42n, -42n, 2n ** 31n, -(2n ** 31n), 2n ** 32n + 7n, 2n ** 63n - 1n, -(2n ** 63n)]; | ||
| for (let i = 0; i < 2e4; ++i) { | ||
| const o = (i & 7) * 8; | ||
| const v = values[i % values.length]; | ||
| shouldBe(writeBigInt64LE(buf, v, o), o + 8, "writeBigInt64LE result"); | ||
| shouldBe(dv.getBigInt64(o, true), v, "writeBigInt64LE store"); | ||
| shouldBe(writeBigInt64BE(buf, v, o), o + 8, "writeBigInt64BE result"); | ||
| shouldBe(dv.getBigInt64(o, false), v, "writeBigInt64BE store"); | ||
| if (v >= 0n) { | ||
| shouldBe(writeBigUInt64LE(buf, v, o), o + 8, "writeBigUInt64LE result"); | ||
| shouldBe(dv.getBigUint64(o, true), v, "writeBigUInt64LE store"); | ||
| shouldBe(writeBigUInt64BE(buf, v, o), o + 8, "writeBigUInt64BE result"); | ||
| shouldBe(dv.getBigUint64(o, false), v, "writeBigUInt64BE store"); | ||
| } | ||
| } | ||
|
|
||
| for (let i = 0; i < 2e4; ++i) { | ||
| shouldBe(writeBigUInt64LE(buf, 2n ** 64n - 1n, 0), 8, "unsigned max"); | ||
| shouldBe(dv.getBigUint64(0, true), 2n ** 64n - 1n, "unsigned max store"); | ||
| shouldThrow(() => writeBigUInt64LE(buf, -1n, 0), RangeError, "unsigned negative"); | ||
| shouldThrow(() => writeBigUInt64LE(buf, 2n ** 64n, 0), RangeError, "unsigned too big"); | ||
| shouldThrow(() => writeBigInt64LE(buf, 2n ** 63n, 0), RangeError, "signed too big"); | ||
| shouldThrow(() => writeBigInt64LE(buf, -(2n ** 63n) - 1n, 0), RangeError, "signed too small"); | ||
| shouldThrow(() => writeBigInt64LE(buf, 2n ** 100n, 0), RangeError, "way too big"); | ||
| shouldThrow(() => writeBigInt64LE(buf, 5, 0), TypeError, "a number is not a BigInt"); | ||
| shouldThrow(() => writeBigInt64LE(buf, 0n, 57), RangeError, "out of bounds"); | ||
| shouldBe(dv.getBigInt64(0, true), -1n, "the failed writes stored nothing"); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| //@ requireOptions("--useDollarVM=1") | ||
|
|
||
| function shouldThrow(f, expected, message) { | ||
| let error = null; | ||
| try { | ||
| f(); | ||
| } catch (e) { | ||
| error = e; | ||
| } | ||
| if (!error) throw new Error(message + ": expected a " + expected.name + " but got no throw"); | ||
| if (!(error instanceof expected)) throw new Error(message + ": expected a " + expected.name + " but got " + error); | ||
| } | ||
| function shouldBe(actual, expected, message) { | ||
| if (actual !== expected) throw new Error(message + ": expected " + expected + " but got " + actual); | ||
| } | ||
|
|
||
| const accessors = $vm.createBufferAccessors(); | ||
| class Buffer extends Uint8Array {} | ||
| Object.assign(Buffer.prototype, accessors); | ||
|
|
||
| const buf = new Buffer(16); | ||
| const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); | ||
|
|
||
| function readInt32LE(b, o) { | ||
| return b.readInt32LE(o); | ||
| } | ||
| noInline(readInt32LE); | ||
| function readUInt8(b, o) { | ||
| return b.readUInt8(o); | ||
| } | ||
| noInline(readUInt8); | ||
| for (let i = 0; i < 1e4; ++i) { | ||
| dv.setInt32(12, i, true); | ||
| shouldBe(readInt32LE(buf, 12), i, "last valid offset"); | ||
| shouldThrow(() => readInt32LE(buf, 13), RangeError, "one past the last valid offset"); | ||
| shouldThrow(() => readInt32LE(buf, 16), RangeError, "offset === length"); | ||
| shouldThrow(() => readInt32LE(buf, -1), RangeError, "negative offset"); | ||
| shouldThrow(() => readInt32LE(buf, 1.5), RangeError, "fractional offset"); | ||
| shouldThrow(() => readInt32LE(buf, NaN), RangeError, "NaN offset"); | ||
| shouldThrow(() => readInt32LE(buf, Infinity), RangeError, "Infinity offset"); | ||
| shouldThrow(() => readInt32LE(buf, "0"), RangeError, "string offset"); | ||
| shouldBe(readInt32LE(buf, 4.0), dv.getInt32(4, true), "integral double offset"); | ||
| shouldBe(readUInt8(buf, 15), buf[15], "last byte"); | ||
| shouldThrow(() => readUInt8(buf, 16), RangeError, "one-byte read one past the end"); | ||
| } | ||
|
|
||
| function writeInt8(b, v, o) { | ||
| return b.writeInt8(v, o); | ||
| } | ||
| noInline(writeInt8); | ||
| function writeUInt16BE(b, v, o) { | ||
| return b.writeUInt16BE(v, o); | ||
| } | ||
| noInline(writeUInt16BE); | ||
| function writeUInt32LE(b, v, o) { | ||
| return b.writeUInt32LE(v, o); | ||
| } | ||
| noInline(writeUInt32LE); | ||
| for (let i = 0; i < 1e4; ++i) { | ||
| shouldBe(writeInt8(buf, 127, 3), 4, "writeInt8 max"); | ||
| shouldBe(dv.getInt8(3), 127, "writeInt8 max store"); | ||
| shouldBe(writeInt8(buf, -128, 3), 4, "writeInt8 min"); | ||
| shouldBe(dv.getInt8(3), -128, "writeInt8 min store"); | ||
| shouldThrow(() => writeInt8(buf, 128, 3), RangeError, "writeInt8 too big"); | ||
| shouldThrow(() => writeInt8(buf, -129, 3), RangeError, "writeInt8 too small"); | ||
| shouldBe(dv.getInt8(3), -128, "a throwing writeInt8 stores nothing"); | ||
| shouldThrow(() => writeUInt16BE(buf, -1, 2), RangeError, "writeUInt16BE negative"); | ||
| shouldThrow(() => writeUInt16BE(buf, 65536, 2), RangeError, "writeUInt16BE too big"); | ||
| shouldBe(writeUInt16BE(buf, 65535, 2), 4, "writeUInt16BE max"); | ||
| shouldBe(dv.getUint16(2, false), 65535, "writeUInt16BE max store"); | ||
| shouldThrow(() => writeUInt32LE(buf, -1, 4), RangeError, "writeUInt32LE negative"); | ||
| shouldThrow(() => writeUInt32LE(buf, 4294967296, 4), RangeError, "writeUInt32LE too big"); | ||
| shouldThrow(() => writeUInt32LE(buf, 4294967295, 14), RangeError, "writeUInt32LE out of bounds"); | ||
| shouldBe(writeUInt32LE(buf, 4294967295, 4), 8, "writeUInt32LE max"); | ||
| shouldBe(dv.getUint32(4, true), 4294967295, "writeUInt32LE max store"); | ||
| } | ||
|
|
||
| { | ||
| const floats = new Float64Array(4); | ||
| const dataView = new DataView(new ArrayBuffer(8)); | ||
| const otherView = new Uint32Array(4); | ||
| function readOnAnything(b, o) { | ||
| return accessors.readInt32LE.call(b, o); | ||
| } | ||
| noInline(readOnAnything); | ||
| for (let i = 0; i < 1e4; ++i) { | ||
| floats[0] = i; | ||
| shouldBe(readOnAnything(buf, 0), dv.getInt32(0, true), "Buffer receiver"); | ||
| shouldBe(readOnAnything(floats, 0), new DataView(floats.buffer).getInt32(0, true), "Float64Array receiver"); | ||
| shouldBe(readOnAnything(dataView, 4), 0, "DataView receiver"); | ||
| shouldBe(readOnAnything(otherView, 12), 0, "Uint32Array receiver (byte semantics)"); | ||
| shouldThrow(() => readOnAnything({}, 0), TypeError, "plain object receiver"); | ||
| shouldThrow(() => readOnAnything(null, 0), TypeError, "null receiver"); | ||
| } | ||
| } | ||
|
|
||
| { | ||
| const detached = new Buffer(16); | ||
| function readDetached(b) { | ||
| return b.readUInt16LE(0); | ||
| } | ||
| noInline(readDetached); | ||
| for (let i = 0; i < 1e3; ++i) shouldBe(readDetached(detached), 0, "before detach"); | ||
| transferArrayBuffer(detached.buffer); | ||
| for (let i = 0; i < 1e3; ++i) shouldThrow(() => readDetached(detached), RangeError, "after detach"); | ||
| } | ||
|
|
||
| { | ||
| let calls = 0; | ||
| const value = { | ||
| valueOf() { | ||
| calls++; | ||
| return 5; | ||
| }, | ||
| }; | ||
| function writeWithBadOffset(b, o) { | ||
| return b.writeInt32LE(value, o); | ||
| } | ||
| noInline(writeWithBadOffset); | ||
| for (let i = 0; i < 1e3; ++i) { | ||
| shouldBe(writeWithBadOffset(buf, 0), 4, "good offset"); | ||
| shouldThrow(() => writeWithBadOffset(buf, 100), RangeError, "bad offset"); | ||
| } | ||
| shouldBe(calls, 2000, "valueOf calls"); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| //@ requireOptions("--useDollarVM=1") | ||
|
|
||
| function shouldBe(actual, expected, message) { | ||
| if (actual !== expected) throw new Error(message + ": expected " + expected + " but got " + actual); | ||
| } | ||
| function shouldThrow(f, expected, message) { | ||
| let error = null; | ||
| try { | ||
| f(); | ||
| } catch (e) { | ||
| error = e; | ||
| } | ||
| if (!(error instanceof expected)) throw new Error(message + ": expected a " + expected.name + " but got " + error); | ||
| } | ||
|
|
||
| const accessors = $vm.createBufferAccessors(); | ||
| class Buffer extends Uint8Array {} | ||
| Object.assign(Buffer.prototype, accessors); | ||
|
|
||
| function readUInt16LE(b, o) { | ||
| return b.readUInt16LE(o); | ||
| } | ||
| noInline(readUInt16LE); | ||
| function writeUInt16LE(b, v, o) { | ||
| return b.writeUInt16LE(v, o); | ||
| } | ||
| noInline(writeUInt16LE); | ||
|
|
||
| { | ||
| const fixed = new Buffer(64); | ||
| for (let i = 0; i < 1e4; ++i) { | ||
| shouldBe(writeUInt16LE(fixed, i & 0xffff, i & 62), (i & 62) + 2, "fixed write"); | ||
| shouldBe(readUInt16LE(fixed, i & 62), i & 0xffff, "fixed read"); | ||
| } | ||
| } | ||
|
|
||
| { | ||
| const rab = new ArrayBuffer(16, { maxByteLength: 64 }); | ||
| const tracking = new Buffer(rab); | ||
| shouldBe(tracking.length, 16, "tracking length"); | ||
| for (let i = 0; i < 1e4; ++i) { | ||
| shouldBe(writeUInt16LE(tracking, i & 0xffff, 14), 16, "tracking write at the end"); | ||
| shouldBe(readUInt16LE(tracking, 14), i & 0xffff, "tracking read at the end"); | ||
| shouldThrow(() => readUInt16LE(tracking, 15), RangeError, "tracking read straddling the end"); | ||
| shouldThrow(() => readUInt16LE(tracking, 16), RangeError, "tracking read past the end"); | ||
| } | ||
| rab.resize(64); | ||
| shouldBe(tracking.length, 64, "grown tracking length"); | ||
| for (let i = 0; i < 1e4; ++i) { | ||
| shouldBe(writeUInt16LE(tracking, i & 0xffff, 62), 64, "write near the grown end"); | ||
| shouldBe(readUInt16LE(tracking, 62), i & 0xffff, "read near the grown end"); | ||
| } | ||
| rab.resize(8); | ||
| shouldBe(tracking.length, 8, "shrunk tracking length"); | ||
| for (let i = 0; i < 1e3; ++i) { | ||
| shouldBe(readUInt16LE(tracking, 6), 0, "read near the shrunk end (never written)"); | ||
| shouldThrow(() => readUInt16LE(tracking, 7), RangeError, "read straddling the shrunk end"); | ||
| shouldThrow(() => writeUInt16LE(tracking, 0, 62), RangeError, "write past the shrunk end"); | ||
| } | ||
| } | ||
|
|
||
| { | ||
| const rab = new ArrayBuffer(32, { maxByteLength: 64 }); | ||
| const fixed = new Buffer(rab, 8, 16); | ||
| shouldBe(fixed.length, 16, "fixed-length view length"); | ||
| for (let i = 0; i < 1e4; ++i) { | ||
| shouldBe(writeUInt16LE(fixed, i & 0xffff, 14), 16, "fixed-length view write"); | ||
| shouldBe(readUInt16LE(fixed, 14), i & 0xffff, "fixed-length view read"); | ||
| } | ||
| rab.resize(16); | ||
| for (let i = 0; i < 1e3; ++i) { | ||
| shouldThrow(() => readUInt16LE(fixed, 0), RangeError, "out-of-bounds view read"); | ||
| shouldThrow(() => writeUInt16LE(fixed, 0, 0), RangeError, "out-of-bounds view write"); | ||
| } | ||
| } | ||
|
|
||
| { | ||
| const gsab = new SharedArrayBuffer(16, { maxByteLength: 64 }); | ||
| const shared = new Buffer(gsab); | ||
| for (let i = 0; i < 1e4; ++i) { | ||
| shouldBe(writeUInt16LE(shared, i & 0xffff, 14), 16, "shared write at the end"); | ||
| shouldBe(readUInt16LE(shared, 14), i & 0xffff, "shared read at the end"); | ||
| shouldThrow(() => readUInt16LE(shared, 15), RangeError, "shared read past the end"); | ||
| } | ||
| gsab.grow(64); | ||
| for (let i = 0; i < 1e4; ++i) { | ||
| shouldBe(writeUInt16LE(shared, i & 0xffff, 62), 64, "write near the grown shared end"); | ||
| shouldBe(readUInt16LE(shared, 62), i & 0xffff, "read near the grown shared end"); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| //@ requireOptions("--useDollarVM=1") | ||
|
|
||
| function shouldBe(actual, expected, message) { | ||
| if (actual !== expected) throw new Error(message + ": expected " + expected + " but got " + actual); | ||
| } | ||
| function shouldThrow(f, expected, message) { | ||
| let error = null; | ||
| try { | ||
| f(); | ||
| } catch (e) { | ||
| error = e; | ||
| } | ||
| if (!(error instanceof expected)) throw new Error(message + ": expected a " + expected.name + " but got " + error); | ||
| } | ||
|
|
||
| class Buffer extends Uint8Array {} | ||
| Object.assign(Buffer.prototype, $vm.createBufferAccessors()); | ||
|
|
||
| const buf = new Buffer(64); | ||
| const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); | ||
| for (let i = 0; i < buf.length; ++i) buf[i] = (i * 71 + 5) & 0xff; | ||
|
|
||
| function readIntLE(b, o, l) { | ||
| return b.readIntLE(o, l); | ||
| } | ||
| function readIntBE(b, o, l) { | ||
| return b.readIntBE(o, l); | ||
| } | ||
| function readUIntLE(b, o, l) { | ||
| return b.readUIntLE(o, l); | ||
| } | ||
| function readUIntBE(b, o, l) { | ||
| return b.readUIntBE(o, l); | ||
| } | ||
| function writeIntLE(b, v, o, l) { | ||
| return b.writeIntLE(v, o, l); | ||
| } | ||
| function writeUIntBE(b, v, o, l) { | ||
| return b.writeUIntBE(v, o, l); | ||
| } | ||
| function readInt32ConstLE(b, o) { | ||
| return b.readIntLE(o, 4); | ||
| } | ||
| function readUInt16ConstBE(b, o) { | ||
| return b.readUIntBE(o, 2); | ||
| } | ||
| function readUInt24ConstLE(b, o) { | ||
| return b.readUIntLE(o, 3); | ||
| } | ||
| function writeInt8Const(b, v, o) { | ||
| return b.writeIntLE(v, o, 1); | ||
| } | ||
| for (const f of [ | ||
| readIntLE, | ||
| readIntBE, | ||
| readUIntLE, | ||
| readUIntBE, | ||
| writeIntLE, | ||
| writeUIntBE, | ||
| readInt32ConstLE, | ||
| readUInt16ConstBE, | ||
| readUInt24ConstLE, | ||
| writeInt8Const, | ||
| ]) | ||
| noInline(f); | ||
|
|
||
| const uint = (o, l, le) => { | ||
| let value = 0; | ||
| for (let i = 0; i < l; ++i) value = le ? value + buf[o + i] * 2 ** (8 * i) : value * 256 + buf[o + i]; | ||
| return value; | ||
| }; | ||
| const sint = (o, l, le) => { | ||
| const value = uint(o, l, le); | ||
| return value >= 2 ** (8 * l - 1) ? value - 2 ** (8 * l) : value; | ||
| }; | ||
|
|
||
| for (let i = 0; i < 1e4; ++i) { | ||
| const o = i & 15; | ||
| for (const l of [1, 2, 3, 4, 5, 6]) { | ||
| shouldBe(readIntLE(buf, o, l), sint(o, l, true), "readIntLE " + l); | ||
| shouldBe(readIntBE(buf, o, l), sint(o, l, false), "readIntBE " + l); | ||
| shouldBe(readUIntLE(buf, o, l), uint(o, l, true), "readUIntLE " + l); | ||
| shouldBe(readUIntBE(buf, o, l), uint(o, l, false), "readUIntBE " + l); | ||
| } | ||
| shouldBe(readInt32ConstLE(buf, o), dv.getInt32(o, true), "readIntLE const 4"); | ||
| shouldBe(readUInt16ConstBE(buf, o), dv.getUint16(o, false), "readUIntBE const 2"); | ||
| shouldBe(readUInt24ConstLE(buf, o), uint(o, 3, true), "readUIntLE const 3"); | ||
| } | ||
|
|
||
| const scratch = new Buffer(64); | ||
| const scratchDV = new DataView(scratch.buffer); | ||
| for (let i = 0; i < 1e4; ++i) { | ||
| const o = i & 15; | ||
| shouldBe(writeIntLE(scratch, -(i & 0x7fff), o, 4), o + 4, "writeIntLE 4 result"); | ||
| shouldBe(scratchDV.getInt32(o, true), -(i & 0x7fff), "writeIntLE 4 store"); | ||
| shouldBe(writeUIntBE(scratch, i & 0xffffff, o, 3), o + 3, "writeUIntBE 3 result"); | ||
| shouldBe(scratch[o] * 65536 + scratch[o + 1] * 256 + scratch[o + 2], i & 0xffffff, "writeUIntBE 3 store"); | ||
| shouldBe(writeInt8Const(scratch, (i & 0xff) - 128, o), o + 1, "writeIntLE const 1 result"); | ||
| shouldBe(scratchDV.getInt8(o), (i & 0xff) - 128, "writeIntLE const 1 store"); | ||
| } | ||
|
|
||
| for (let i = 0; i < 3e3; ++i) { | ||
| shouldThrow(() => readIntLE(buf, 0, 7), RangeError, "byteLength 7"); | ||
| shouldThrow(() => readIntLE(buf, 0, 0), RangeError, "byteLength 0"); | ||
| shouldThrow(() => readInt32ConstLE(buf, undefined), TypeError, "undefined offset"); | ||
| shouldThrow(() => readInt32ConstLE(buf, 61), RangeError, "out of bounds"); | ||
| shouldThrow(() => writeInt8Const(scratch, 128, 0), RangeError, "value out of range"); | ||
| shouldThrow(() => writeUIntBE(scratch, 2 ** 24, 0, 3), RangeError, "3-byte value out of range"); | ||
| shouldThrow(() => writeIntLE(scratch, NaN, 0, 4), RangeError, "NaN value"); | ||
| shouldThrow(() => writeIntLE(scratch, Infinity, 0, 4), RangeError, "Infinity value"); | ||
| shouldThrow(() => writeIntLE(scratch, -Infinity, 0, 4), RangeError, "-Infinity value"); | ||
| shouldThrow(() => writeInt8Const(scratch, NaN, 0), RangeError, "NaN value, constant width"); | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.