From 4658dfa03d669251a6cccffd9ba8bf2bdb8cd3bb Mon Sep 17 00:00:00 2001 From: stampy88 Date: Wed, 19 Mar 2025 15:09:03 -0400 Subject: [PATCH] Added the rest of the write methods - added the rest of the `Buffer.write*` methods. Also updated the `assert._isSameValue` to handle number specifically to handle floating point differences that are within a precision. --- assert.js | 39 +- buffer/buffer.go | 360 ++++++++++++++++- buffer/buffer_test.go | 708 ++++++++++++++++++++++++++++++++++ buffer/testdata/assertions.js | 2 + errors/errors.go | 4 +- goutil/argtypes.go | 15 +- 6 files changed, 1117 insertions(+), 11 deletions(-) diff --git a/assert.js b/assert.js index 516719f..6fda5f9 100644 --- a/assert.js +++ b/assert.js @@ -2,13 +2,15 @@ const assert = { _isSameValue(a, b) { - if (a === b) { - // Handle +/-0 vs. -/+0 - return a !== 0 || 1 / a === 1 / b; + if (this._isNumber(a)) { + return this._numberEquals(a, b); } - - // Handle NaN vs. NaN - return a !== a && b !== b; + + return a === b; + }, + + _isNumber(val) { + return typeof val === "number"; }, _toString(value) { @@ -26,6 +28,31 @@ const assert = { throw err; } }, + + _numberEquals(a, b, precision = 1e-6) { + if (!this._isNumber(b)) { + return false; + } + // Handle NaN vs. NaN + if (a !== a && b !== b) { + return true; // Both are NaN + } + // If only one is NaN, they're not equal + if (a !== a || b !== b) { + return false; + } + if (a === b) { + // Handle +/-0 vs. -/+0 + return a !== 0 || 1 / a === 1 / b; + } + // Use relative error for larger numbers, absolute for smaller ones + if (Math.abs(a) > 1 || Math.abs(b) > 1) { + return Math.abs((a - b) / Math.max(Math.abs(a), Math.abs(b))) < precision; + } + + // Absolute error for small numbers + return Math.abs(a - b) < precision; + }, sameValue(actual, expected, message) { if (assert._isSameValue(actual, expected)) { diff --git a/buffer/buffer.go b/buffer/buffer.go index a7dd5b5..f7d2bdc 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -718,6 +718,260 @@ func (b *Buffer) writeBigUInt64LE(call goja.FunctionCall) goja.Value { return b.r.ToValue(offset + 8) } +// writeDoubleBE writes a big-endian 64-bit double to the buffer +func (b *Buffer) writeDoubleBE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredFloatArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 8) + + bits := math.Float64bits(value) + binary.BigEndian.PutUint64(bb[offset:offset+8], bits) + + return b.r.ToValue(offset + 8) +} + +// writeDoubleLE writes a little-endian 64-bit double to the buffer +func (b *Buffer) writeDoubleLE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredFloatArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 8) + + bits := math.Float64bits(value) + binary.LittleEndian.PutUint64(bb[offset:offset+8], bits) + + return b.r.ToValue(offset + 8) +} + +// writeFloatBE writes a big-endian 32-bit float to the buffer +func (b *Buffer) writeFloatBE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredFloatArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 4) + + b.ensureWithinFloat32Range(value) + + bits := math.Float32bits(float32(value)) + binary.BigEndian.PutUint32(bb[offset:offset+4], bits) + + return b.r.ToValue(offset + 4) +} + +// writeFloatLE writes a little-endian 32-bit floating-point number to the buffer +func (b *Buffer) writeFloatLE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredFloatArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 4) + + b.ensureWithinFloat32Range(value) + + bits := math.Float32bits(float32(value)) + binary.LittleEndian.PutUint32(bb[offset:offset+4], bits) + + return b.r.ToValue(offset + 4) +} + +// writeInt8 writes an 8-bit signed integer to the buffer +func (b *Buffer) writeInt8(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 1) + + if value < math.MinInt8 || value > math.MaxInt8 { + panic(errors.NewArgumentOutOfRangeError(b.r, "value", value)) + } + + bb[offset] = byte(int8(value)) + + return b.r.ToValue(offset + 1) +} + +// writeInt16BE writes a big-endian 16-bit signed integer to the buffer +func (b *Buffer) writeInt16BE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 2) + + b.ensureWithinInt16Range(value) + + binary.BigEndian.PutUint16(bb[offset:offset+2], uint16(value)) + + return b.r.ToValue(offset + 2) +} + +// writeInt16LE writes a little-endian 16-bit signed integer to the buffer +func (b *Buffer) writeInt16LE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 2) + + b.ensureWithinInt16Range(value) + + binary.LittleEndian.PutUint16(bb[offset:offset+2], uint16(value)) + + return b.r.ToValue(offset + 2) +} + +// writeInt32BE writes a big-endian 32-bit signed integer to the buffer +func (b *Buffer) writeInt32BE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 4) + + b.ensureWithinInt32Range(value) + + binary.BigEndian.PutUint32(bb[offset:offset+4], uint32(value)) + + return b.r.ToValue(offset + 4) +} + +// writeInt32LE writes a little-endian 32-bit signed integer to the buffer +func (b *Buffer) writeInt32LE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 4) + + b.ensureWithinInt32Range(value) + + binary.LittleEndian.PutUint32(bb[offset:offset+4], uint32(value)) + + return b.r.ToValue(offset + 4) +} + +// writeIntBE writes a big-endian signed integer of variable byte length +func (b *Buffer) writeIntBE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset, byteLength := b.getVariableLengthWriteArguments(call, bb) + + b.ensureWithinIntRange(byteLength, value) + + // Write bytes in big-endian order (most significant byte first) + for i := int64(0); i < byteLength; i++ { + shift := uint(8 * (byteLength - 1 - i)) + bb[offset+i] = byte(value >> shift) + } + + return b.r.ToValue(offset + byteLength) +} + +// writeIntLE writes a little-endian signed integer of variable byte length +func (b *Buffer) writeIntLE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset, byteLength := b.getVariableLengthWriteArguments(call, bb) + + b.ensureWithinIntRange(byteLength, value) + + // Write bytes in little-endian order + for i := int64(0); i < byteLength; i++ { + shift := uint(8 * i) + bb[offset+i] = byte(value >> shift) + } + + return b.r.ToValue(offset + byteLength) +} + +// writeUInt8 writes an 8-bit unsigned integer to the buffer +func (b *Buffer) writeUInt8(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 1) + + if value < 0 || value > 255 { + panic(errors.NewArgumentOutOfRangeError(b.r, "value", value)) + } + + bb[offset] = uint8(value) + + return b.r.ToValue(offset + 1) +} + +// writeUInt16BE writes a big-endian 16-bit unsigned integer to the buffer +func (b *Buffer) writeUInt16BE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 2) + + b.ensureWithinUInt16Range(value) + + binary.BigEndian.PutUint16(bb[offset:offset+2], uint16(value)) + + return b.r.ToValue(offset + 2) +} + +// writeUInt16LE writes a little-endian 16-bit unsigned integer to the buffer +func (b *Buffer) writeUInt16LE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 2) + + b.ensureWithinUInt16Range(value) + + binary.LittleEndian.PutUint16(bb[offset:offset+2], uint16(value)) + + return b.r.ToValue(offset + 2) +} + +// writeUInt32BE writes a big-endian 32-bit unsigned integer to the buffer +func (b *Buffer) writeUInt32BE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 4) + + b.ensureWithinUInt32Range(value) + + binary.BigEndian.PutUint32(bb[offset:offset+4], uint32(value)) + + return b.r.ToValue(offset + 4) +} + +// writeUInt32LE writes a little-endian 32-bit unsigned integer to the buffer +func (b *Buffer) writeUInt32LE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset := b.getOffsetArgument(call, 1, bb, 4) + + b.ensureWithinUInt32Range(value) + + binary.LittleEndian.PutUint32(bb[offset:offset+4], uint32(value)) + + return b.r.ToValue(offset + 4) +} + +// writeUIntBE writes a big-endian unsigned integer of variable byte length +func (b *Buffer) writeUIntBE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset, byteLength := b.getVariableLengthWriteArguments(call, bb) + + b.ensureWithinUIntRange(byteLength, value) + + // Write the bytes in big-endian order (most significant byte first) + for i := int64(0); i < byteLength; i++ { + shift := (byteLength - 1 - i) * 8 + bb[offset+i] = byte(value >> shift) + } + + return b.r.ToValue(offset + byteLength) +} + +// writeUIntLE writes a little-endian unsigned integer of variable byte length +func (b *Buffer) writeUIntLE(call goja.FunctionCall) goja.Value { + bb := Bytes(b.r, call.This) + value := goutil.RequiredIntegerArgument(b.r, call, "value", 0) + offset, byteLength := b.getVariableLengthWriteArguments(call, bb) + + b.ensureWithinUIntRange(byteLength, value) + + // Write the bytes in little-endian order + for i := int64(0); i < byteLength; i++ { + shift := uint(8 * i) + bb[offset+i] = byte(value >> shift) + } + + return b.r.ToValue(offset + byteLength) +} + func (b *Buffer) getOffsetArgument(call goja.FunctionCall, argIndex int, bb []byte, numBytes int64) int64 { offset := goutil.OptionalIntegerArgument(b.r, call, "offset", argIndex, 0) @@ -729,8 +983,16 @@ func (b *Buffer) getOffsetArgument(call goja.FunctionCall, argIndex int, bb []by } func (b *Buffer) getVariableLengthReadArguments(call goja.FunctionCall, bb []byte) (int64, int64) { - offset := goutil.RequiredIntegerArgument(b.r, call, "offset", 0) - byteLength := goutil.RequiredIntegerArgument(b.r, call, "byteLength", 1) + return b.getVariableLengthArguments(call, bb, 0, 1) +} + +func (b *Buffer) getVariableLengthWriteArguments(call goja.FunctionCall, bb []byte) (int64, int64) { + return b.getVariableLengthArguments(call, bb, 1, 2) +} + +func (b *Buffer) getVariableLengthArguments(call goja.FunctionCall, bb []byte, offsetArgIndex, byteLengthArgIndex int) (int64, int64) { + offset := goutil.RequiredIntegerArgument(b.r, call, "offset", offsetArgIndex) + byteLength := goutil.RequiredIntegerArgument(b.r, call, "byteLength", byteLengthArgIndex) if byteLength < 1 || byteLength > 6 { panic(errors.NewArgumentOutOfRangeError(b.r, "byteLength", byteLength)) @@ -742,6 +1004,61 @@ func (b *Buffer) getVariableLengthReadArguments(call goja.FunctionCall, bb []byt return offset, byteLength } +func (b *Buffer) ensureWithinFloat32Range(value float64) { + if value < -math.MaxFloat32 || value > math.MaxFloat32 { + panic(errors.NewArgumentOutOfRangeError(b.r, "value", value)) + } +} + +func (b *Buffer) ensureWithinInt16Range(value int64) { + if value < math.MinInt16 || value > math.MaxInt16 { + panic(errors.NewArgumentOutOfRangeError(b.r, "value", value)) + } +} + +func (b *Buffer) ensureWithinInt32Range(value int64) { + if value < math.MinInt32 || value > math.MaxInt32 { + panic(errors.NewArgumentOutOfRangeError(b.r, "value", value)) + } +} + +// ensureWithinIntRange checks to make sure that value is within the integer range +// defined by the byteLength. Note that byteLength can be at most 6 bytes, so a +// 48 bit integer is the largest possible value. +func (b *Buffer) ensureWithinIntRange(byteLength, value int64) { + // Calculate the valid range for the given byte length + bits := byteLength * 8 + minValue := -(int64(1) << (bits - 1)) + maxValue := (int64(1) << (bits - 1)) - 1 + + if value < minValue || value > maxValue { + panic(errors.NewArgumentOutOfRangeError(b.r, "value", value)) + } +} + +func (b *Buffer) ensureWithinUInt16Range(value int64) { + if value < 0 || value > math.MaxUint16 { + panic(errors.NewArgumentOutOfRangeError(b.r, "value", value)) + } +} + +func (b *Buffer) ensureWithinUInt32Range(value int64) { + if value < 0 || value > math.MaxUint32 { + panic(errors.NewArgumentOutOfRangeError(b.r, "value", value)) + } +} + +// ensureWithinUIntRange checks to make sure that value is within the unsigned integer +// range defined by the byteLength. Note that byteLength can be at most 6 bytes, so a +// 48 bit unsigned integer is the largest possible value. +func (b *Buffer) ensureWithinUIntRange(byteLength, value int64) { + // Validate that the value is within the valid range for the given byteLength + maxValue := (int64(1) << (8 * byteLength)) - 1 + if value < 0 || value > maxValue { + panic(errors.NewArgumentOutOfRangeError(b.r, "value", value)) + } +} + func signExtend(value int64, numBytes int64) int64 { // we don't have to turn this to a uint64 first because numBytes < 8 so // the sign bit will never pushed out of the int64 range @@ -828,6 +1145,45 @@ func Require(runtime *goja.Runtime, module *goja.Object) { // aliases for writeBigUInt64LE proto.Set("writeBigUint64LE", b.writeBigUInt64LE) + proto.Set("writeDoubleBE", b.writeDoubleBE) + proto.Set("writeDoubleLE", b.writeDoubleLE) + proto.Set("writeFloatBE", b.writeFloatBE) + proto.Set("writeFloatLE", b.writeFloatLE) + proto.Set("writeInt8", b.writeInt8) + proto.Set("writeInt16BE", b.writeInt16BE) + proto.Set("writeInt16LE", b.writeInt16LE) + proto.Set("writeInt32BE", b.writeInt32BE) + proto.Set("writeInt32LE", b.writeInt32LE) + proto.Set("writeIntBE", b.writeIntBE) + proto.Set("writeIntLE", b.writeIntLE) + proto.Set("writeUInt8", b.writeUInt8) + // aliases for writeUInt8 + proto.Set("writeUint8", b.writeUInt8) + + proto.Set("writeUInt16BE", b.writeUInt16BE) + // aliases for writeUInt16BE + proto.Set("writeUint16BE", b.writeUInt16BE) + + proto.Set("writeUInt16LE", b.writeUInt16LE) + // aliases for writeUInt16LE + proto.Set("writeUint16LE", b.writeUInt16LE) + + proto.Set("writeUInt32BE", b.writeUInt32BE) + // aliases for writeUInt32BE + proto.Set("writeUint32BE", b.writeUInt32BE) + + proto.Set("writeUInt32LE", b.writeUInt32LE) + // aliases for writeUInt32LE + proto.Set("writeUint32LE", b.writeUInt32LE) + + proto.Set("writeUIntBE", b.writeUIntBE) + // aliases for writeUIntBE + proto.Set("writeUintBE", b.writeUIntBE) + + proto.Set("writeUIntLE", b.writeUIntLE) + // aliases for writeUIntLE + proto.Set("writeUintLE", b.writeUIntLE) + ctor.Set("prototype", proto) ctor.Set("poolSize", 8192) ctor.Set("from", b.from) diff --git a/buffer/buffer_test.go b/buffer/buffer_test.go index 1e69f01..0450db3 100644 --- a/buffer/buffer_test.go +++ b/buffer/buffer_test.go @@ -1720,3 +1720,711 @@ func TestBuffer_writeBigUInt64LE(t *testing.T) { runTestCases(t, tcs) } + +func TestBuffer_writeDoubleBE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(8); + assert.throwsNodeErrorWithMessage(() => buf.writeDoubleBE(123.456, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 1 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(24); + buf.writeDoubleBE(123.1); // default offset of zero + buf.writeDoubleBE(456.4, 8); + buf.writeDoubleBE(789.7, 16); + + assertValueRead(buf.readDoubleBE(0), 123.1); + assertValueRead(buf.readDoubleBE(8), 456.4); + assertValueRead(buf.readDoubleBE(16), 789.7); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeDoubleLE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(8); + assert.throwsNodeErrorWithMessage(() => buf.writeDoubleLE(123.456, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 1 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(24); + buf.writeDoubleLE(123.1); // default offset of zero + buf.writeDoubleLE(456.4, 8); + buf.writeDoubleLE(789.7, 16); + + assertValueRead(buf.readDoubleLE(0), 123.1); + assertValueRead(buf.readDoubleLE(8), 456.4); + assertValueRead(buf.readDoubleLE(16), 789.7); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeFloatBE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(4); + assert.throwsNodeErrorWithMessage(() => buf.writeFloatBE(123.456, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 1 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(4); + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeFloatBE(3.5e+38, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 3.5e+38 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeFloatBE(-3.5e+38, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -3.5e+38 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(12); + buf.writeFloatBE(123.1); // default offset of zero + buf.writeFloatBE(456.4, 4); + buf.writeFloatBE(789.7, 8); + + assertValueRead(buf.readFloatBE(0), 123.1); + assertValueRead(buf.readFloatBE(4), 456.4); + assertValueRead(buf.readFloatBE(8), 789.7); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeFloatLE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(4); + assert.throwsNodeErrorWithMessage(() => buf.writeFloatLE(123.456, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 1 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(4); + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeFloatLE(3.5e+38, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 3.5e+38 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeFloatLE(-3.5e+38, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -3.5e+38 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(12); + buf.writeFloatLE(123.1); // default offset of zero + buf.writeFloatLE(456.4, 4); + buf.writeFloatLE(789.7, 8); + + assertValueRead(buf.readFloatLE(0), 123.1); + assertValueRead(buf.readFloatLE(4), 456.4); + assertValueRead(buf.readFloatLE(8), 789.7); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeInt8(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(2); + assert.throwsNodeErrorWithMessage(() => buf.writeInt8(2, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 2 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(4); + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeInt8(128), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 128 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeInt8(-129), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -129 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(3); + buf.writeInt8(3); // default offset of zero + buf.writeInt8(2, 1); + buf.writeInt8(1, 2); + + assertValueRead(buf.readInt8(0), 3); + assertValueRead(buf.readInt8(1), 2); + assertValueRead(buf.readInt8(2), 1); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeInt16BE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(2); + assert.throwsNodeErrorWithMessage(() => buf.writeInt16BE(2, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 1 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(4); + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeInt16BE(32768), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 32768 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeInt16BE(-32769), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -32769 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(6); + buf.writeInt16BE(3); // default offset of zero + buf.writeInt16BE(2, 2); + buf.writeInt16BE(1, 4); + + assertValueRead(buf.readInt16BE(0), 3); + assertValueRead(buf.readInt16BE(2), 2); + assertValueRead(buf.readInt16BE(4), 1); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeInt16LE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(2); + assert.throwsNodeErrorWithMessage(() => buf.writeInt16LE(2, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 1 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(4); + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeInt16LE(32768), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 32768 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeInt16LE(-32769), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -32769 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(6); + buf.writeInt16LE(3); // default offset of zero + buf.writeInt16LE(2, 2); + buf.writeInt16LE(1, 4); + + assertValueRead(buf.readInt16LE(0), 3); + assertValueRead(buf.readInt16LE(2), 2); + assertValueRead(buf.readInt16LE(4), 1); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeInt32BE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(4); + assert.throwsNodeErrorWithMessage(() => buf.writeInt32BE(2, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 1 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(4); + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeInt32BE(2147483648), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 2147483648 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeInt32BE(-2147483649), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -2147483649 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(12); + buf.writeInt32BE(3); // default offset of zero + buf.writeInt32BE(2, 4); + buf.writeInt32BE(1, 8); + + assertValueRead(buf.readInt32BE(0), 3); + assertValueRead(buf.readInt32BE(4), 2); + assertValueRead(buf.readInt32BE(8), 1); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeInt32LE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(4); + assert.throwsNodeErrorWithMessage(() => buf.writeInt32LE(2, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 1 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(4); + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeInt32LE(2147483648), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 2147483648 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeInt32LE(-2147483649), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -2147483649 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(12); + buf.writeInt32LE(3); // default offset of zero + buf.writeInt32LE(2, 4); + buf.writeInt32LE(1, 8); + + assertValueRead(buf.readInt32LE(0), 3); + assertValueRead(buf.readInt32LE(4), 2); + assertValueRead(buf.readInt32LE(8), 1); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeIntBE(t *testing.T) { + tcs := []testCase{ + { + name: "write out of range offset", + script: ` + const buf = Buffer.alloc(6); + assert.throwsNodeErrorWithMessage(() => buf.writeIntBE(127, 6, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 6 is out of range.'); + `, + }, + { + name: "byteLength out of range", + script: ` + const buf = Buffer.alloc(6); + + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeIntBE(0, 0, 7), RangeError, "ERR_OUT_OF_RANGE", 'The value of "byteLength" 7 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeIntBE(0, 0, 0), RangeError, "ERR_OUT_OF_RANGE", 'The value of "byteLength" 0 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(6); + // above the 6-byte max + assert.throwsNodeErrorWithMessage(() => buf.writeIntBE(140737488355328, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 140737488355328 is out of range.'); + // below the 6-byte min + assert.throwsNodeErrorWithMessage(() => buf.writeIntBE(-140737488355329, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -140737488355329 is out of range.'); + // above the 2-byte max + assert.throwsNodeErrorWithMessage(() => buf.writeIntBE(32768, 0, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 32768 is out of range.'); + // below the 2-byte min + assert.throwsNodeErrorWithMessage(() => buf.writeIntBE(-32769, 0, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -32769 is out of range.'); + `, + }, + { + name: "writing and reading with different byte lengths", + script: ` + const buf = Buffer.alloc(6); + buf.writeIntBE(-1, 0, 1); // 1 byte + buf.writeIntBE(256, 1, 2); // 2 bytes + buf.writeIntBE(97328, 3, 3); // 3 bytes + + assertValueRead(buf.readIntBE(0, 1), -1); + assertValueRead(buf.toString('hex', 0, 1), "ff"); + assertValueRead(buf.readIntBE(1, 2), 256); + assertValueRead(buf.toString('hex', 1, 3), "0100"); + assertValueRead(buf.readIntBE(3, 3), 97328); + assertValueRead(buf.toString('hex', 3, 6), "017c30"); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeIntLE(t *testing.T) { + tcs := []testCase{ + { + name: "write out of range offset", + script: ` + const buf = Buffer.alloc(6); + assert.throwsNodeErrorWithMessage(() => buf.writeIntLE(127, 6, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 6 is out of range.'); + `, + }, + { + name: "byteLength out of range", + script: ` + const buf = Buffer.alloc(6); + + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeIntLE(0, 0, 7), RangeError, "ERR_OUT_OF_RANGE", 'The value of "byteLength" 7 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeIntLE(0, 0, 0), RangeError, "ERR_OUT_OF_RANGE", 'The value of "byteLength" 0 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(6); + // above the 6-byte max + assert.throwsNodeErrorWithMessage(() => buf.writeIntLE(140737488355328, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 140737488355328 is out of range.'); + // below the 6-byte min + assert.throwsNodeErrorWithMessage(() => buf.writeIntLE(-140737488355329, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -140737488355329 is out of range.'); + // above the 2-byte max + assert.throwsNodeErrorWithMessage(() => buf.writeIntLE(32768, 0, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 32768 is out of range.'); + // below the 2-byte min + assert.throwsNodeErrorWithMessage(() => buf.writeIntLE(-32769, 0, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -32769 is out of range.'); + `, + }, + { + name: "writing and reading with different byte lengths", + script: ` + const buf = Buffer.alloc(6); + buf.writeIntLE(-1, 0, 1); // 1 byte + buf.writeIntLE(256, 1, 2); // 2 bytes + buf.writeIntLE(97328, 3, 3); // 3 bytes + + assertValueRead(buf.readIntLE(0, 1), -1); + assertValueRead(buf.toString('hex', 0, 1), "ff"); + assertValueRead(buf.readIntLE(1, 2), 256); + assertValueRead(buf.toString('hex', 1, 3), "0001"); + assertValueRead(buf.readIntLE(3, 3), 97328); + assertValueRead(buf.toString('hex', 3, 6), "307c01"); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeUInt8(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(2); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt8(2, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 2 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(2); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt8(256), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 256 is out of range.'); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt8(256), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 256 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(3); + buf.writeUInt8(3); // default offset of zero + buf.writeUint8(2, 1); // using alias + buf.writeUInt8(1, 2); + + assertValueRead(buf.readUInt8(0), 3); + assertValueRead(buf.readUInt8(1), 2); + assertValueRead(buf.readUInt8(2), 1); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeUInt16BE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(2); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt16BE(2, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 2 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(2); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt16BE(65536), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 65536 is out of range.'); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt16BE(-1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -1 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(6); + buf.writeUInt16BE(3); // default offset of zero + buf.writeUInt16BE(2, 2); // using method alias + buf.writeUInt16BE(1, 4); + + assertValueRead(buf.readUInt16BE(0), 3); + assertValueRead(buf.readUInt16BE(2), 2); + assertValueRead(buf.readUInt16BE(4), 1); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeUInt16LE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(2); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt16LE(2, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 2 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(2); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt16LE(65536), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 65536 is out of range.'); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt16LE(-1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -1 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(6); + buf.writeUInt16LE(3); // default offset of zero + buf.writeUint16LE(2, 2); // using method alias + buf.writeUInt16LE(1, 4); + + assertValueRead(buf.readUInt16LE(0), 3); + assertValueRead(buf.readUInt16LE(2), 2); + assertValueRead(buf.readUInt16LE(4), 1); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeUInt32BE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(4); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt32BE(2, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 2 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(4); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt32BE(4294967296), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 4294967296 is out of range.'); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt32BE(-1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -1 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(12); + buf.writeUInt32BE(3); // default offset of zero + buf.writeUint32BE(2, 4); // using method alias + buf.writeUInt32BE(1, 8); + + assertValueRead(buf.readUInt32BE(0), 3); + assertValueRead(buf.readUInt32BE(4), 2); + assertValueRead(buf.readUInt32BE(8), 1); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeUInt32LE(t *testing.T) { + tcs := []testCase{ + { + name: "write with out of range offset", + script: ` + const buf = Buffer.alloc(4); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt32LE(2, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 2 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(4); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt32LE(4294967296), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 4294967296 is out of range.'); + assert.throwsNodeErrorWithMessage(() => buf.writeUInt32LE(-1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -1 is out of range.'); + `, + }, + { + name: "writing and then reading different sections of buffer", + script: ` + const buf = Buffer.alloc(12); + buf.writeUInt32LE(3); // default offset of zero + buf.writeUInt32LE(2, 4); // using method alias + buf.writeUInt32LE(1, 8); + + assertValueRead(buf.readUInt32LE(0), 3); + assertValueRead(buf.readUInt32LE(4), 2); + assertValueRead(buf.readUInt32LE(8), 1); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeUIntBE(t *testing.T) { + tcs := []testCase{ + { + name: "write out of range offset", + script: ` + const buf = Buffer.alloc(6); + assert.throwsNodeErrorWithMessage(() => buf.writeUIntBE(127, 6, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 6 is out of range.'); + `, + }, + { + name: "byteLength out of range", + script: ` + const buf = Buffer.alloc(6); + + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeUIntBE(0, 0, 7), RangeError, "ERR_OUT_OF_RANGE", 'The value of "byteLength" 7 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeUIntBE(0, 0, 0), RangeError, "ERR_OUT_OF_RANGE", 'The value of "byteLength" 0 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(6); + // above the 6-byte max + assert.throwsNodeErrorWithMessage(() => buf.writeUIntBE(281474976710656, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 281474976710656 is out of range.'); + // below the 6-byte min + assert.throwsNodeErrorWithMessage(() => buf.writeUIntBE(-1, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -1 is out of range.'); + // above the 2-byte max + assert.throwsNodeErrorWithMessage(() => buf.writeUIntBE(65536, 0, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 65536 is out of range.'); + // below the 2-byte min + assert.throwsNodeErrorWithMessage(() => buf.writeUIntBE(-1, 0, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -1 is out of range.'); + `, + }, + { + name: "writing and reading with different byte lengths", + script: ` + const buf = Buffer.alloc(6); + buf.writeUIntBE(1, 0, 1); // 1 byte + buf.writeUintBE(256, 1, 2); // 2 bytes + buf.writeUIntBE(97328, 3, 3); // 3 bytes + + assertValueRead(buf.readUIntBE(0, 1), 1); + assertValueRead(buf.toString('hex', 0, 1), "01"); + assertValueRead(buf.readUIntBE(1, 2), 256); + assertValueRead(buf.toString('hex', 1, 3), "0100"); + assertValueRead(buf.readUIntBE(3, 3), 97328); + assertValueRead(buf.toString('hex', 3, 6), "017c30"); + `, + }, + } + + runTestCases(t, tcs) +} + +func TestBuffer_writeUIntLE(t *testing.T) { + tcs := []testCase{ + { + name: "write out of range offset", + script: ` + const buf = Buffer.alloc(6); + assert.throwsNodeErrorWithMessage(() => buf.writeUIntLE(127, 6, 1), RangeError, "ERR_OUT_OF_RANGE", 'The value of "offset" 6 is out of range.'); + `, + }, + { + name: "byteLength out of range", + script: ` + const buf = Buffer.alloc(6); + + // above the max + assert.throwsNodeErrorWithMessage(() => buf.writeUIntLE(0, 0, 7), RangeError, "ERR_OUT_OF_RANGE", 'The value of "byteLength" 7 is out of range.'); + // below the min + assert.throwsNodeErrorWithMessage(() => buf.writeUIntLE(0, 0, 0), RangeError, "ERR_OUT_OF_RANGE", 'The value of "byteLength" 0 is out of range.'); + `, + }, + { + name: "number exceeds ranges", + script: ` + const buf = Buffer.alloc(6); + // above the 6-byte max + assert.throwsNodeErrorWithMessage(() => buf.writeUIntLE(281474976710656, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 281474976710656 is out of range.'); + // below the 6-byte min + assert.throwsNodeErrorWithMessage(() => buf.writeUIntLE(-1, 0, 6), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -1 is out of range.'); + // above the 2-byte max + assert.throwsNodeErrorWithMessage(() => buf.writeUIntLE(65536, 0, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" 65536 is out of range.'); + // below the 2-byte min + assert.throwsNodeErrorWithMessage(() => buf.writeUIntLE(-1, 0, 2), RangeError, "ERR_OUT_OF_RANGE", 'The value of "value" -1 is out of range.'); + `, + }, + { + name: "writing and reading with different byte lengths", + script: ` + const buf = Buffer.alloc(6); + buf.writeUIntLE(1, 0, 1); // 1 byte + buf.writeUintLE(256, 1, 2); // 2 bytes + buf.writeUIntLE(97328, 3, 3); // 3 bytes + + assertValueRead(buf.readUIntLE(0, 1), 1); + assertValueRead(buf.toString('hex', 0, 1), "01"); + assertValueRead(buf.readUIntLE(1, 2), 256); + assertValueRead(buf.toString('hex', 1, 3), "0001"); + assertValueRead(buf.readUIntLE(3, 3), 97328); + assertValueRead(buf.toString('hex', 3, 6), "307c01"); + `, + }, + } + + runTestCases(t, tcs) +} diff --git a/buffer/testdata/assertions.js b/buffer/testdata/assertions.js index 54fcab1..68fc695 100644 --- a/buffer/testdata/assertions.js +++ b/buffer/testdata/assertions.js @@ -25,7 +25,9 @@ function assertBufferWriteRead(buffer, writeMethod, readMethod, value, offset = // getBufferElementSize determines the number of bytes per type based on method name function getBufferElementSize(methodName) { if (methodName.includes('64')) return 8; + if (methodName.includes('Double')) return 8; if (methodName.includes('32')) return 4; + if (methodName.includes('Float')) return 4; if (methodName.includes('16')) return 2; if (methodName.includes('8')) return 1; return 1; diff --git a/errors/errors.go b/errors/errors.go index c3d06ff..6a99035 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -92,6 +92,6 @@ func NewNotCorrectTypeError(r *goja.Runtime, name, _type string) *goja.Object { return NewTypeError(r, ErrCodeInvalidArgType, "The \"%s\" argument must be of type %s.", name, _type) } -func NewArgumentOutOfRangeError(r *goja.Runtime, name string, v int64) *goja.Object { - return NewRangeError(r, ErrCodeOutOfRange, "The value of \"%s\" %d is out of range.", name, v) +func NewArgumentOutOfRangeError(r *goja.Runtime, name string, v any) *goja.Object { + return NewRangeError(r, ErrCodeOutOfRange, "The value of \"%s\" %v is out of range.", name, v) } diff --git a/goutil/argtypes.go b/goutil/argtypes.go index 4e6d86a..90293ea 100644 --- a/goutil/argtypes.go +++ b/goutil/argtypes.go @@ -1,9 +1,10 @@ package goutil import ( + "math/big" + "github.com/dop251/goja" "github.com/dop251/goja_nodejs/errors" - "math/big" ) func RequiredIntegerArgument(r *goja.Runtime, call goja.FunctionCall, name string, argIndex int) int64 { @@ -18,6 +19,18 @@ func RequiredIntegerArgument(r *goja.Runtime, call goja.FunctionCall, name strin panic(errors.NewArgumentNotNumberTypeError(r, name)) } +func RequiredFloatArgument(r *goja.Runtime, call goja.FunctionCall, name string, argIndex int) float64 { + arg := call.Argument(argIndex) + if goja.IsNumber(arg) { + return arg.ToFloat() + } + if goja.IsUndefined(arg) { + panic(errors.NewTypeError(r, errors.ErrCodeInvalidArgType, "The \"%s\" argument is required.", name)) + } + + panic(errors.NewArgumentNotNumberTypeError(r, name)) +} + func CoercedIntegerArgument(call goja.FunctionCall, argIndex int, defaultValue int64, typeMistMatchValue int64) int64 { arg := call.Argument(argIndex) if goja.IsNumber(arg) {