From 47935a80f81173bfdd1011ae6125073a4eef962a Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 2 Mar 2021 17:02:40 -0500 Subject: [PATCH] fix: Throw error on bigint usage and add helpers to Long Adds helpers toBigInt and fromBigInt to the Long class to help support 64bit values. Throws an error when bigint is attempted to be serialized. NODE-2378 --- lib/bson/long.js | 14 +++++++++ lib/bson/parser/serializer.js | 6 ++++ test/node/bigint_test.js | 59 +++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 test/node/bigint_test.js diff --git a/lib/bson/long.js b/lib/bson/long.js index 78215aa34..551d1a604 100644 --- a/lib/bson/long.js +++ b/lib/bson/long.js @@ -77,6 +77,11 @@ Long.prototype.toNumber = function() { return this.high_ * Long.TWO_PWR_32_DBL_ + this.getLowBitsUnsigned(); }; +/** Converts the Long to a BigInt (arbitrary precision). */ +Long.prototype.toBigInt = function () { + return BigInt(this.toString()); +} + /** * Return the JSON value. * @@ -711,6 +716,15 @@ Long.fromNumber = function(value) { } }; +/** + * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. + * @param {bigint} value - The number in question + * @returns {Long} The corresponding Long value + */ +Long.fromBigInt = function(value) { + return Long.fromString(value.toString(10), 10); +} + /** * Returns a Long representing the 64-bit integer that comes by concatenating the given high and low bits. Each is assumed to use 32 bits. * diff --git a/lib/bson/parser/serializer.js b/lib/bson/parser/serializer.js index 6bb212ed9..fab9b6f3c 100644 --- a/lib/bson/parser/serializer.js +++ b/lib/bson/parser/serializer.js @@ -709,6 +709,8 @@ var serializeInto = function serializeInto( index = serializeString(buffer, key, value, index, true); } else if (type === 'number') { index = serializeNumber(buffer, key, value, index, true); + } else if(type === 'bigint') { + throw new TypeError('Unsupported type BigInt, please use Decimal128'); } else if (type === 'boolean') { index = serializeBoolean(buffer, key, value, index, true); } else if (value instanceof Date || isDate(value)) { @@ -820,6 +822,8 @@ var serializeInto = function serializeInto( index = serializeString(buffer, key, value, index); } else if (type === 'number') { index = serializeNumber(buffer, key, value, index); + } else if(type === 'bigint') { + throw new TypeError('Unsupported type BigInt, please use Decimal128'); } else if (type === 'boolean') { index = serializeBoolean(buffer, key, value, index); } else if (value instanceof Date || isDate(value)) { @@ -923,6 +927,8 @@ var serializeInto = function serializeInto( index = serializeString(buffer, key, value, index); } else if (type === 'number') { index = serializeNumber(buffer, key, value, index); + } else if(type === 'bigint') { + throw new TypeError('Unsupported type BigInt, please use Decimal128'); } else if (type === 'boolean') { index = serializeBoolean(buffer, key, value, index); } else if (value instanceof Date || isDate(value)) { diff --git a/test/node/bigint_test.js b/test/node/bigint_test.js new file mode 100644 index 000000000..2c9154944 --- /dev/null +++ b/test/node/bigint_test.js @@ -0,0 +1,59 @@ +/* globals BigInt */ +'use strict'; + +const createBSON = require('../utils'); +const BSON = require('../..'); +const bson = createBSON(); + +try { + BigInt(0); + + // will throw on the line above if BigInt is not supported in the runtime + + exports['Should error on serialize bigint'] = function (test) { + const testDoc = { b: BigInt(32) }; + try { + bson.serialize(testDoc) + test.ok(false); + } catch (error) { + test.ok(error instanceof TypeError); + test.ok(error.message === 'Unsupported type BigInt, please use Decimal128'); + } + test.done(); + }; + + exports['Should error on serialize bigint inside array'] = function (test) { + const testDoc = { b: [0, 1, BigInt(0x1ffffffff)] }; + try { + bson.serialize(testDoc) + test.ok(false); + } catch (error) { + test.ok(error instanceof TypeError); + test.ok(error.message === 'Unsupported type BigInt, please use Decimal128'); + } + test.done(); + }; + + exports['Should error on serialize bigint inside subdocument'] = function (test) { + const testDoc = { b: { a: BigInt(0x1ffffffff) } }; + try { + bson.serialize(testDoc) + test.ok(false); + } catch (error) { + test.ok(error instanceof TypeError); + test.ok(error.message === 'Unsupported type BigInt, please use Decimal128'); + } + test.done(); + }; + + exports['Should support conversion on Long type'] = function (test) { + const long = BSON.Long.fromBigInt(BigInt(200)); + test.ok(long._bsontype === 'Long'); + test.ok(long.toNumber() === 200); + test.ok(long.toBigInt() === BigInt(200)); + test.done(); + } + +} catch (_) { + // 'JS VM does not support BigInt' +}