From d51cee0164610c7ea8373e7d40577f8d632ee997 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 2 Mar 2021 17:22:07 -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 78215aa3..551d1a60 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 6bb212ed..fab9b6f3 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 00000000..701e064a --- /dev/null +++ b/test/node/bigint_test.js @@ -0,0 +1,59 @@ +/* globals BigInt */ +'use strict'; + +var createBSON = require('../utils'); +var BSON = require('../..'); +var 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) { + var 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) { + var 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) { + var 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) { + var 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' +}