From f8cc0a435a5253fa750a31bdc19abe17d4ccd644 Mon Sep 17 00:00:00 2001 From: LijieZhang1998 Date: Tue, 21 Oct 2025 16:37:15 -0500 Subject: [PATCH 1/2] Add old shell compatibility for Timestamp for JS Engine --- snippets/mongocompat/mongotypes.js | 55 ++++++++++++++++++++++++++++++ snippets/mongocompat/test.js | 43 +++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/snippets/mongocompat/mongotypes.js b/snippets/mongocompat/mongotypes.js index 13dfc3d..8e428a8 100644 --- a/snippets/mongocompat/mongotypes.js +++ b/snippets/mongocompat/mongotypes.js @@ -1,5 +1,60 @@ // Date and time types if (typeof (Timestamp) != "undefined") { + const OriginalTimestamp = Timestamp; + + // Reference: https://github.com/10gen/mongo/blob/master/src/mongo/scripting/mozjs/timestamp.cpp:67-78 + function validateTimestampComponent(component, name) { + const MAX_UINT32 = 4294967295; + + if (typeof component !== 'number') { + throw new Error(name + " must be a number"); + } + + const val = Math.floor(component); + if (val < 0 || val > MAX_UINT32) { + throw new Error( + name + " must be non-negative and not greater than " + MAX_UINT32 + ", got " + val + ); + } + + return val; + } + + Timestamp = function(t, i) { + if (arguments.length === 0) { + return new OriginalTimestamp({ t: 0, i: 0 }); + } + + if (arguments.length === 1) { + const proto = Object.getPrototypeOf(t); + if ((proto === null || proto === Object.prototype) && ('t' in t || 'i' in t)) { + var validatedT = validateTimestampComponent(t.t || 0, "Timestamp time (seconds)"); + var validatedI = validateTimestampComponent(t.i || 0, "Timestamp increment"); + return new OriginalTimestamp({ t: validatedT, i: validatedI }); + } + return new OriginalTimestamp(t); + } + + // Reference: https://github.com/10gen/mongo/blob/master/src/mongo/scripting/mozjs/timestamp.cpp:91-98 + if (arguments.length === 2) { + const validatedT = validateTimestampComponent(t, "Timestamp time (seconds)"); + const validatedI = validateTimestampComponent(i, "Timestamp increment"); + return new OriginalTimestamp({ t: validatedT, i: validatedI }); + } + + throw new Error("Timestamp needs 0 or 2 arguments"); + }; + + Timestamp.prototype = OriginalTimestamp.prototype; + + var staticProps = Object.getOwnPropertyNames(OriginalTimestamp); + for (var key of staticProps) { + // Skip prototype, length, name(function internals) + if (key !== 'prototype' && key !== 'length' && key !== 'name') { + Timestamp[key] = OriginalTimestamp[key]; + } + } + Timestamp.prototype.tojson = function() { return this.toStringIncomparable(); }; diff --git a/snippets/mongocompat/test.js b/snippets/mongocompat/test.js index f54223f..61c0213 100644 --- a/snippets/mongocompat/test.js +++ b/snippets/mongocompat/test.js @@ -22,6 +22,49 @@ assert.strictEqual(minLong.bottom, 0); assert.strictEqual(minLong.exactValueString, "-9223372036854775808"); const nl2 = NumberLong("200"); assert.strictEqual(maxLong.compare(nl2), 1); + const decimal = NumberDecimal("1.1"); assert.strictEqual(decimal.toString(), 'NumberDecimal("1.1")'); assert.strictEqual(decimal.tojson(), 'NumberDecimal("1.1")'); + +const ts1 = Timestamp(); +assert.strictEqual(ts1.toString(), 'Timestamp(0, 0)'); +const ts2 = Timestamp(100, 200); +assert.strictEqual(ts2.toString(), 'Timestamp(100, 200)'); +const ts3 = Timestamp(1.9, 2.1); +assert.strictEqual(ts3.toString(), 'Timestamp(1, 2)'); +try { + Timestamp(-1, 0); + assert.fail('Should throw for negative time'); +} catch (e) { + assert(e.message.includes('must be non-negative')); +} +try { + Timestamp(0, 5000000000); + assert.fail('Should throw for i > uint32 max'); +} catch (e) { + assert(e.message.includes('not greater than 4294967295')); +} +const ts4 = Timestamp(123, 456); +assert.strictEqual(ts4.toString(), 'Timestamp(123, 456)'); +assert.strictEqual(ts4.tojson(), 'Timestamp(123, 456)'); +assert.strictEqual(ts4.getTime(), 123); +assert.strictEqual(ts4.getInc(), 456); +assert.strictEqual(ts4._bsontype, 'Timestamp'); +const tsFromBits = Timestamp.fromBits(100, 200); +assert.strictEqual(tsFromBits.i, 100); +assert.strictEqual(tsFromBits.t, 200); +assert.strictEqual(tsFromBits.toString(), 'Timestamp(200, 100)'); +const tsFromInt = Timestamp.fromInt(12345); +assert.strictEqual(tsFromInt._bsontype, 'Timestamp'); +assert.strictEqual(tsFromInt.i, 12345); +assert.strictEqual(tsFromInt.t, 0); +const tsFromNum = Timestamp.fromNumber(67890); +assert.strictEqual(tsFromNum._bsontype, 'Timestamp'); +assert.strictEqual(tsFromNum.i, 67890); +assert.strictEqual(tsFromNum.t, 0); +const tsFromStr = Timestamp.fromString('ff', 16); +assert.strictEqual(tsFromStr.i, 255); +assert.strictEqual(tsFromStr.t, 0); +assert.strictEqual(Timestamp.MAX_VALUE._bsontype, 'Long'); +assert.strictEqual(Timestamp.MAX_VALUE, Long.MAX_UNSIGNED_VALUE); From 2880cd571390cf4e6ec661057e095f7edca65ae4 Mon Sep 17 00:00:00 2001 From: LijieZhang1998 Date: Wed, 22 Oct 2025 10:56:52 -0500 Subject: [PATCH 2/2] Refactor per comments --- snippets/mongocompat/mongotypes.js | 17 ++++++++--------- snippets/mongocompat/test.js | 2 ++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/snippets/mongocompat/mongotypes.js b/snippets/mongocompat/mongotypes.js index 8e428a8..8a8ec34 100644 --- a/snippets/mongocompat/mongotypes.js +++ b/snippets/mongocompat/mongotypes.js @@ -2,18 +2,18 @@ if (typeof (Timestamp) != "undefined") { const OriginalTimestamp = Timestamp; - // Reference: https://github.com/10gen/mongo/blob/master/src/mongo/scripting/mozjs/timestamp.cpp:67-78 + // Reference: https://github.com/mongodb/mongo/blob/c4d21d3346572e28df2f174df4d87e7618df4a77/src/mongo/scripting/mozjs/timestamp.cpp#L67-L78 function validateTimestampComponent(component, name) { const MAX_UINT32 = 4294967295; if (typeof component !== 'number') { - throw new Error(name + " must be a number"); + throw new TypeError(`${name} must be a number`); } const val = Math.floor(component); if (val < 0 || val > MAX_UINT32) { - throw new Error( - name + " must be non-negative and not greater than " + MAX_UINT32 + ", got " + val + throw new TypeError( + `${name} must be non-negative and not greater than ${MAX_UINT32}, got ${val}` ); } @@ -28,14 +28,14 @@ if (typeof (Timestamp) != "undefined") { if (arguments.length === 1) { const proto = Object.getPrototypeOf(t); if ((proto === null || proto === Object.prototype) && ('t' in t || 'i' in t)) { - var validatedT = validateTimestampComponent(t.t || 0, "Timestamp time (seconds)"); - var validatedI = validateTimestampComponent(t.i || 0, "Timestamp increment"); + const validatedT = validateTimestampComponent(t.t || 0, "Timestamp time (seconds)"); + const validatedI = validateTimestampComponent(t.i || 0, "Timestamp increment"); return new OriginalTimestamp({ t: validatedT, i: validatedI }); } return new OriginalTimestamp(t); } - // Reference: https://github.com/10gen/mongo/blob/master/src/mongo/scripting/mozjs/timestamp.cpp:91-98 + // Reference: https://github.com/mongodb/mongo/blob/c4d21d3346572e28df2f174df4d87e7618df4a77/src/mongo/scripting/mozjs/timestamp.cpp#L91-L98 if (arguments.length === 2) { const validatedT = validateTimestampComponent(t, "Timestamp time (seconds)"); const validatedI = validateTimestampComponent(i, "Timestamp increment"); @@ -47,8 +47,7 @@ if (typeof (Timestamp) != "undefined") { Timestamp.prototype = OriginalTimestamp.prototype; - var staticProps = Object.getOwnPropertyNames(OriginalTimestamp); - for (var key of staticProps) { + for (const key of Object.getOwnPropertyNames(OriginalTimestamp)) { // Skip prototype, length, name(function internals) if (key !== 'prototype' && key !== 'length' && key !== 'name') { Timestamp[key] = OriginalTimestamp[key]; diff --git a/snippets/mongocompat/test.js b/snippets/mongocompat/test.js index 61c0213..df4b8db 100644 --- a/snippets/mongocompat/test.js +++ b/snippets/mongocompat/test.js @@ -46,12 +46,14 @@ try { assert(e.message.includes('not greater than 4294967295')); } const ts4 = Timestamp(123, 456); +assert(ts4 instanceof Timestamp); assert.strictEqual(ts4.toString(), 'Timestamp(123, 456)'); assert.strictEqual(ts4.tojson(), 'Timestamp(123, 456)'); assert.strictEqual(ts4.getTime(), 123); assert.strictEqual(ts4.getInc(), 456); assert.strictEqual(ts4._bsontype, 'Timestamp'); const tsFromBits = Timestamp.fromBits(100, 200); +assert(tsFromBits instanceof Timestamp); assert.strictEqual(tsFromBits.i, 100); assert.strictEqual(tsFromBits.t, 200); assert.strictEqual(tsFromBits.toString(), 'Timestamp(200, 100)');