diff options
author | Adam Chelminski <adam.chelminski@mongodb.com> | 2016-08-05 11:49:17 -0400 |
---|---|---|
committer | Adam Chelminski <adam.chelminski@mongodb.com> | 2016-08-15 10:52:24 -0400 |
commit | df4248d69bd6643925d0e9cc62c6be3adf7750d7 (patch) | |
tree | 1f6b84bc55c83a7c981f74ed7483dc087b7b23f6 /src | |
parent | 9175ab505d970a6e97733a28e16496426d10f3ee (diff) | |
download | mongo-df4248d69bd6643925d0e9cc62c6be3adf7750d7.tar.gz |
SERVER-24146 Fix JSON.stringify regression, and add new "tostrictjson" shell util function for printing strict JSON
Diffstat (limited to 'src')
21 files changed, 176 insertions, 24 deletions
diff --git a/src/mongo/scripting/mozjs/bindata.cpp b/src/mongo/scripting/mozjs/bindata.cpp index d7bacb5558e..6632b1ed315 100644 --- a/src/mongo/scripting/mozjs/bindata.cpp +++ b/src/mongo/scripting/mozjs/bindata.cpp @@ -46,10 +46,11 @@ namespace mongo { namespace mozjs { -const JSFunctionSpec BinDataInfo::methods[4] = { +const JSFunctionSpec BinDataInfo::methods[5] = { MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(base64, BinDataInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(hex, BinDataInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(toString, BinDataInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(toJSON, BinDataInfo), JS_FS_END, }; @@ -163,6 +164,21 @@ void BinDataInfo::Functions::toString::call(JSContext* cx, JS::CallArgs args) { ValueReader(cx, args.rval()).fromStringData(ss.operator std::string()); } +void BinDataInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args) { + ObjectWrapper o(cx, args.thisv()); + + auto data_str = getEncoded(args.thisv()); + + std::stringstream ss; + ss << std::hex; + ss.width(2); + ss.fill('0'); + ss << o.getNumber(InternedString::type); + + ValueReader(cx, args.rval()) + .fromBSON(BSON("$binary" << *data_str << "$type" << ss.str()), nullptr, false); +} + void BinDataInfo::Functions::base64::call(JSContext* cx, JS::CallArgs args) { auto str = getEncoded(args.thisv()); diff --git a/src/mongo/scripting/mozjs/bindata.h b/src/mongo/scripting/mozjs/bindata.h index 57a4f158949..ae3c8f40c28 100644 --- a/src/mongo/scripting/mozjs/bindata.h +++ b/src/mongo/scripting/mozjs/bindata.h @@ -46,13 +46,14 @@ struct BinDataInfo : public BaseInfo { MONGO_DECLARE_JS_FUNCTION(base64); MONGO_DECLARE_JS_FUNCTION(hex); MONGO_DECLARE_JS_FUNCTION(toString); + MONGO_DECLARE_JS_FUNCTION(toJSON); MONGO_DECLARE_JS_FUNCTION(HexData); MONGO_DECLARE_JS_FUNCTION(MD5); MONGO_DECLARE_JS_FUNCTION(UUID); }; - static const JSFunctionSpec methods[4]; + static const JSFunctionSpec methods[5]; static const JSFunctionSpec freeFunctions[4]; static const char* const className; diff --git a/src/mongo/scripting/mozjs/internedstring.defs b/src/mongo/scripting/mozjs/internedstring.defs index 40bf0b97498..792ec0bbcb5 100644 --- a/src/mongo/scripting/mozjs/internedstring.defs +++ b/src/mongo/scripting/mozjs/internedstring.defs @@ -18,6 +18,7 @@ MONGO_MOZJS_INTERNED_STRING(dollar_db, "$db") MONGO_MOZJS_INTERNED_STRING(dollar_id, "$id") MONGO_MOZJS_INTERNED_STRING(dollar_ref, "$ref") MONGO_MOZJS_INTERNED_STRING(_fields, "_fields") +MONGO_MOZJS_INTERNED_STRING(flags, "flags") MONGO_MOZJS_INTERNED_STRING(floatApprox, "floatApprox") MONGO_MOZJS_INTERNED_STRING(_fullName, "_fullName") MONGO_MOZJS_INTERNED_STRING(getCollection, "getCollection") @@ -37,6 +38,7 @@ MONGO_MOZJS_INTERNED_STRING(_ns, "_ns") MONGO_MOZJS_INTERNED_STRING(ns, "ns") MONGO_MOZJS_INTERNED_STRING(_numReturned, "_numReturned") MONGO_MOZJS_INTERNED_STRING(_options, "_options") +MONGO_MOZJS_INTERNED_STRING(prototype, "prototype") MONGO_MOZJS_INTERNED_STRING(_query, "_query") MONGO_MOZJS_INTERNED_STRING(readOnly, "readOnly") MONGO_MOZJS_INTERNED_STRING(_ro, "_ro") @@ -45,6 +47,7 @@ MONGO_MOZJS_INTERNED_STRING(_shortName, "_shortName") MONGO_MOZJS_INTERNED_STRING(singleton, "singleton") MONGO_MOZJS_INTERNED_STRING(_skip, "_skip") MONGO_MOZJS_INTERNED_STRING(slaveOk, "slaveOk") +MONGO_MOZJS_INTERNED_STRING(source, "source") MONGO_MOZJS_INTERNED_STRING(_special, "_special") MONGO_MOZJS_INTERNED_STRING(str, "str") MONGO_MOZJS_INTERNED_STRING(top, "top") diff --git a/src/mongo/scripting/mozjs/maxkey.cpp b/src/mongo/scripting/mozjs/maxkey.cpp index 411f71dd746..db03b3ca555 100644 --- a/src/mongo/scripting/mozjs/maxkey.cpp +++ b/src/mongo/scripting/mozjs/maxkey.cpp @@ -39,8 +39,10 @@ namespace mongo { namespace mozjs { -const JSFunctionSpec MaxKeyInfo::methods[2] = { - MONGO_ATTACH_JS_CONSTRAINED_METHOD(tojson, MaxKeyInfo), JS_FS_END, +const JSFunctionSpec MaxKeyInfo::methods[3] = { + MONGO_ATTACH_JS_CONSTRAINED_METHOD(tojson, MaxKeyInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD(toJSON, MaxKeyInfo), + JS_FS_END, }; const char* const MaxKeyInfo::className = "MaxKey"; @@ -88,6 +90,10 @@ void MaxKeyInfo::Functions::tojson::call(JSContext* cx, JS::CallArgs args) { ValueReader(cx, args.rval()).fromStringData("{ \"$maxKey\" : 1 }"); } +void MaxKeyInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args) { + ValueReader(cx, args.rval()).fromBSON(BSON("$maxKey" << 1), nullptr, false); +} + void MaxKeyInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) { ObjectWrapper protoWrapper(cx, proto); diff --git a/src/mongo/scripting/mozjs/maxkey.h b/src/mongo/scripting/mozjs/maxkey.h index 32c567e9a68..4c9e5ac4abb 100644 --- a/src/mongo/scripting/mozjs/maxkey.h +++ b/src/mongo/scripting/mozjs/maxkey.h @@ -50,9 +50,10 @@ struct MaxKeyInfo : public BaseInfo { struct Functions { MONGO_DECLARE_JS_FUNCTION(tojson); + MONGO_DECLARE_JS_FUNCTION(toJSON); }; - static const JSFunctionSpec methods[2]; + static const JSFunctionSpec methods[3]; static void postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto); diff --git a/src/mongo/scripting/mozjs/minkey.cpp b/src/mongo/scripting/mozjs/minkey.cpp index c85ca2f6b25..7a5786559c5 100644 --- a/src/mongo/scripting/mozjs/minkey.cpp +++ b/src/mongo/scripting/mozjs/minkey.cpp @@ -39,8 +39,10 @@ namespace mongo { namespace mozjs { -const JSFunctionSpec MinKeyInfo::methods[2] = { - MONGO_ATTACH_JS_CONSTRAINED_METHOD(tojson, MinKeyInfo), JS_FS_END, +const JSFunctionSpec MinKeyInfo::methods[3] = { + MONGO_ATTACH_JS_CONSTRAINED_METHOD(tojson, MinKeyInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD(toJSON, MinKeyInfo), + JS_FS_END, }; const char* const MinKeyInfo::className = "MinKey"; @@ -88,6 +90,10 @@ void MinKeyInfo::Functions::tojson::call(JSContext* cx, JS::CallArgs args) { ValueReader(cx, args.rval()).fromStringData("{ \"$minKey\" : 1 }"); } +void MinKeyInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args) { + ValueReader(cx, args.rval()).fromBSON(BSON("$minKey" << 1), nullptr, false); +} + void MinKeyInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) { ObjectWrapper protoWrapper(cx, proto); diff --git a/src/mongo/scripting/mozjs/minkey.h b/src/mongo/scripting/mozjs/minkey.h index 62259747494..439e2a924d1 100644 --- a/src/mongo/scripting/mozjs/minkey.h +++ b/src/mongo/scripting/mozjs/minkey.h @@ -50,9 +50,10 @@ struct MinKeyInfo : public BaseInfo { struct Functions { MONGO_DECLARE_JS_FUNCTION(tojson); + MONGO_DECLARE_JS_FUNCTION(toJSON); }; - static const JSFunctionSpec methods[2]; + static const JSFunctionSpec methods[3]; static void postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto); diff --git a/src/mongo/scripting/mozjs/numberdecimal.cpp b/src/mongo/scripting/mozjs/numberdecimal.cpp index e5243eb6c8e..fcbdf2471c9 100644 --- a/src/mongo/scripting/mozjs/numberdecimal.cpp +++ b/src/mongo/scripting/mozjs/numberdecimal.cpp @@ -42,8 +42,10 @@ namespace mongo { namespace mozjs { -const JSFunctionSpec NumberDecimalInfo::methods[2] = { - MONGO_ATTACH_JS_CONSTRAINED_METHOD(toString, NumberDecimalInfo), JS_FS_END, +const JSFunctionSpec NumberDecimalInfo::methods[3] = { + MONGO_ATTACH_JS_CONSTRAINED_METHOD(toString, NumberDecimalInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD(toJSON, NumberDecimalInfo), + JS_FS_END, }; const char* const NumberDecimalInfo::className = "NumberDecimal"; @@ -76,6 +78,12 @@ void NumberDecimalInfo::Functions::toString::call(JSContext* cx, JS::CallArgs ar ValueReader(cx, args.rval()).fromStringData(ss.operator std::string()); } +void NumberDecimalInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args) { + Decimal128 val = NumberDecimalInfo::ToNumberDecimal(cx, args.thisv()); + + ValueReader(cx, args.rval()).fromBSON(BSON("$numberDecimal" << val.toString()), nullptr, false); +} + void NumberDecimalInfo::construct(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); diff --git a/src/mongo/scripting/mozjs/numberdecimal.h b/src/mongo/scripting/mozjs/numberdecimal.h index 698bd8fe212..19460158cbe 100644 --- a/src/mongo/scripting/mozjs/numberdecimal.h +++ b/src/mongo/scripting/mozjs/numberdecimal.h @@ -46,9 +46,10 @@ struct NumberDecimalInfo : public BaseInfo { struct Functions { MONGO_DECLARE_JS_FUNCTION(toString); + MONGO_DECLARE_JS_FUNCTION(toJSON); }; - static const JSFunctionSpec methods[2]; + static const JSFunctionSpec methods[3]; static const char* const className; static const unsigned classFlags = JSCLASS_HAS_PRIVATE; diff --git a/src/mongo/scripting/mozjs/numberint.cpp b/src/mongo/scripting/mozjs/numberint.cpp index c55510502e9..b4d6af63155 100644 --- a/src/mongo/scripting/mozjs/numberint.cpp +++ b/src/mongo/scripting/mozjs/numberint.cpp @@ -40,9 +40,10 @@ namespace mongo { namespace mozjs { -const JSFunctionSpec NumberIntInfo::methods[4] = { +const JSFunctionSpec NumberIntInfo::methods[5] = { MONGO_ATTACH_JS_CONSTRAINED_METHOD(toNumber, NumberIntInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD(toString, NumberIntInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD(toJSON, NumberIntInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD(valueOf, NumberIntInfo), JS_FS_END, }; @@ -87,6 +88,12 @@ void NumberIntInfo::Functions::toString::call(JSContext* cx, JS::CallArgs args) ValueReader(cx, args.rval()).fromStringData(ss.operator std::string()); } +void NumberIntInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args) { + int val = NumberIntInfo::ToNumberInt(cx, args.thisv()); + + args.rval().setInt32(val); +} + void NumberIntInfo::construct(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); diff --git a/src/mongo/scripting/mozjs/numberint.h b/src/mongo/scripting/mozjs/numberint.h index a9a052bb0b7..7dd2538ba30 100644 --- a/src/mongo/scripting/mozjs/numberint.h +++ b/src/mongo/scripting/mozjs/numberint.h @@ -45,10 +45,11 @@ struct NumberIntInfo : public BaseInfo { struct Functions { MONGO_DECLARE_JS_FUNCTION(toNumber); MONGO_DECLARE_JS_FUNCTION(toString); + MONGO_DECLARE_JS_FUNCTION(toJSON); MONGO_DECLARE_JS_FUNCTION(valueOf); }; - static const JSFunctionSpec methods[4]; + static const JSFunctionSpec methods[5]; static const char* const className; static const unsigned classFlags = JSCLASS_HAS_PRIVATE; diff --git a/src/mongo/scripting/mozjs/numberlong.cpp b/src/mongo/scripting/mozjs/numberlong.cpp index 7bc9ebe10ff..1f763e47c20 100644 --- a/src/mongo/scripting/mozjs/numberlong.cpp +++ b/src/mongo/scripting/mozjs/numberlong.cpp @@ -43,9 +43,10 @@ namespace mongo { namespace mozjs { -const JSFunctionSpec NumberLongInfo::methods[5] = { +const JSFunctionSpec NumberLongInfo::methods[6] = { MONGO_ATTACH_JS_CONSTRAINED_METHOD(toNumber, NumberLongInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD(toString, NumberLongInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD(toJSON, NumberLongInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD(valueOf, NumberLongInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD(compare, NumberLongInfo), JS_FS_END}; @@ -53,7 +54,7 @@ const JSFunctionSpec NumberLongInfo::methods[5] = { const char* const NumberLongInfo::className = "NumberLong"; void NumberLongInfo::finalize(JSFreeOp* fop, JSObject* obj) { - auto numLong = static_cast<int*>(JS_GetPrivate(obj)); + auto numLong = static_cast<int64_t*>(JS_GetPrivate(obj)); if (numLong) delete numLong; @@ -94,6 +95,13 @@ void NumberLongInfo::Functions::toString::call(JSContext* cx, JS::CallArgs args) ValueReader(cx, args.rval()).fromStringData(ss.operator std::string()); } +void NumberLongInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args) { + int64_t val = NumberLongInfo::ToNumberLong(cx, args.thisv()); + + ValueReader(cx, args.rval()) + .fromBSON(BSON("$numberLong" << std::to_string(val)), nullptr, false); +} + void NumberLongInfo::Functions::compare::call(JSContext* cx, JS::CallArgs args) { uassert(ErrorCodes::BadValue, "NumberLong.compare() needs 1 argument", args.length() == 1); uassert(ErrorCodes::BadValue, diff --git a/src/mongo/scripting/mozjs/numberlong.h b/src/mongo/scripting/mozjs/numberlong.h index 22bf2b94790..ed2d850464b 100644 --- a/src/mongo/scripting/mozjs/numberlong.h +++ b/src/mongo/scripting/mozjs/numberlong.h @@ -56,6 +56,7 @@ struct NumberLongInfo : public BaseInfo { struct Functions { MONGO_DECLARE_JS_FUNCTION(toNumber); MONGO_DECLARE_JS_FUNCTION(toString); + MONGO_DECLARE_JS_FUNCTION(toJSON); MONGO_DECLARE_JS_FUNCTION(valueOf); MONGO_DECLARE_JS_FUNCTION(compare); MONGO_DECLARE_JS_FUNCTION(floatApprox); @@ -63,7 +64,7 @@ struct NumberLongInfo : public BaseInfo { MONGO_DECLARE_JS_FUNCTION(bottom); }; - static const JSFunctionSpec methods[5]; + static const JSFunctionSpec methods[6]; static const char* const className; static const unsigned classFlags = JSCLASS_HAS_PRIVATE; diff --git a/src/mongo/scripting/mozjs/oid.cpp b/src/mongo/scripting/mozjs/oid.cpp index afd6345441a..f2db6c80383 100644 --- a/src/mongo/scripting/mozjs/oid.cpp +++ b/src/mongo/scripting/mozjs/oid.cpp @@ -41,8 +41,10 @@ namespace mongo { namespace mozjs { -const JSFunctionSpec OIDInfo::methods[2] = { - MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(toString, OIDInfo), JS_FS_END, +const JSFunctionSpec OIDInfo::methods[3] = { + MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(toString, OIDInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(toJSON, OIDInfo), + JS_FS_END, }; const char* const OIDInfo::className = "ObjectId"; @@ -63,6 +65,12 @@ void OIDInfo::Functions::toString::call(JSContext* cx, JS::CallArgs args) { ValueReader(cx, args.rval()).fromStringData(str); } +void OIDInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args) { + auto oid = static_cast<OID*>(JS_GetPrivate(args.thisv().toObjectOrNull())); + + ValueReader(cx, args.rval()).fromBSON(BSON("$oid" << oid->toString()), nullptr, false); +} + void OIDInfo::Functions::getter::call(JSContext* cx, JS::CallArgs args) { auto oid = static_cast<OID*>(JS_GetPrivate(args.thisv().toObjectOrNull())); diff --git a/src/mongo/scripting/mozjs/oid.h b/src/mongo/scripting/mozjs/oid.h index 3d723b97003..06a54b740bf 100644 --- a/src/mongo/scripting/mozjs/oid.h +++ b/src/mongo/scripting/mozjs/oid.h @@ -45,9 +45,10 @@ struct OIDInfo : public BaseInfo { struct Functions { MONGO_DECLARE_JS_FUNCTION(getter); MONGO_DECLARE_JS_FUNCTION(toString); + MONGO_DECLARE_JS_FUNCTION(toJSON); }; - static const JSFunctionSpec methods[2]; + static const JSFunctionSpec methods[3]; static const unsigned classFlags = JSCLASS_HAS_PRIVATE; diff --git a/src/mongo/scripting/mozjs/regexp.cpp b/src/mongo/scripting/mozjs/regexp.cpp index b935a9fb354..55761dc606d 100644 --- a/src/mongo/scripting/mozjs/regexp.cpp +++ b/src/mongo/scripting/mozjs/regexp.cpp @@ -29,11 +29,27 @@ #include "mongo/platform/basic.h" #include "mongo/scripting/mozjs/regexp.h" +#include "mongo/scripting/mozjs/valuereader.h" +#include "mongo/scripting/mozjs/wrapconstrainedmethod.h" namespace mongo { namespace mozjs { +const JSFunctionSpec RegExpInfo::methods[2] = { + MONGO_ATTACH_JS_FUNCTION(toJSON), JS_FS_END, +}; + const char* const RegExpInfo::className = "RegExp"; +void RegExpInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args) { + ObjectWrapper o(cx, args.thisv()); + + auto regex_string = o.getString(InternedString::source); + auto flags_string = o.getString(InternedString::flags); + + ValueReader(cx, args.rval()) + .fromBSON(BSON("$regex" << regex_string << "$options" << flags_string), nullptr, false); +} + } // namespace mozjs } // namespace mongo diff --git a/src/mongo/scripting/mozjs/regexp.h b/src/mongo/scripting/mozjs/regexp.h index b8553518127..33e11ef4a14 100644 --- a/src/mongo/scripting/mozjs/regexp.h +++ b/src/mongo/scripting/mozjs/regexp.h @@ -36,10 +36,17 @@ namespace mozjs { /** * The "RegExp" Javascript object. * - * Note that this installs over native. We only use this to grab the regexp - * prototype early in case users overwrite it. + * Note that this installs "overNative", so we don't actually do anything other + * than layer our own toJSON function on top of the existing prototype. */ struct RegExpInfo : public BaseInfo { + + struct Functions { + MONGO_DECLARE_JS_FUNCTION(toJSON); + }; + + static const JSFunctionSpec methods[2]; + static const char* const className; static const InstallType installType = InstallType::OverNative; diff --git a/src/mongo/scripting/mozjs/timestamp.cpp b/src/mongo/scripting/mozjs/timestamp.cpp index 99d83f7c343..468c2f4927f 100644 --- a/src/mongo/scripting/mozjs/timestamp.cpp +++ b/src/mongo/scripting/mozjs/timestamp.cpp @@ -38,11 +38,16 @@ #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" #include "mongo/scripting/mozjs/valuewriter.h" +#include "mongo/scripting/mozjs/wrapconstrainedmethod.h" #include "mongo/util/mongoutils/str.h" namespace mongo { namespace mozjs { +const JSFunctionSpec TimestampInfo::methods[2] = { + MONGO_ATTACH_JS_CONSTRAINED_METHOD(toJSON, TimestampInfo), JS_FS_END, +}; + const char* const TimestampInfo::className = "Timestamp"; namespace { @@ -84,5 +89,15 @@ void TimestampInfo::construct(JSContext* cx, JS::CallArgs args) { args.rval().setObjectOrNull(thisv); } +void TimestampInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args) { + ObjectWrapper o(cx, args.thisv()); + + ValueReader(cx, args.rval()) + .fromBSON(BSON("$timestamp" << BSON("t" << o.getNumber(InternedString::t) << "i" + << o.getNumber(InternedString::i))), + nullptr, + false); +} + } // namespace mozjs } // namespace mongo diff --git a/src/mongo/scripting/mozjs/timestamp.h b/src/mongo/scripting/mozjs/timestamp.h index 1dc4a998420..c5cbd869b7b 100644 --- a/src/mongo/scripting/mozjs/timestamp.h +++ b/src/mongo/scripting/mozjs/timestamp.h @@ -46,6 +46,12 @@ namespace mozjs { struct TimestampInfo : public BaseInfo { static void construct(JSContext* cx, JS::CallArgs args); + struct Functions { + MONGO_DECLARE_JS_FUNCTION(toJSON); + }; + + static const JSFunctionSpec methods[2]; + static const char* const className; }; diff --git a/src/mongo/scripting/mozjs/wraptype.h b/src/mongo/scripting/mozjs/wraptype.h index d6fb135da44..76af28efd1c 100644 --- a/src/mongo/scripting/mozjs/wraptype.h +++ b/src/mongo/scripting/mozjs/wraptype.h @@ -226,6 +226,7 @@ public: WrapType(JSContext* context) : _context(context), _proto(), + _constructor(), _jsclass({T::className, T::classFlags, T::addProperty != BaseInfo::addProperty ? smUtils::addProperty<T> : nullptr, @@ -265,6 +266,7 @@ public: ~WrapType() { // Persistent globals don't RAII, you have to reset() them manually _proto.reset(); + _constructor.reset(); } void install(JS::HandleObject global) { @@ -310,7 +312,8 @@ public: void newInstance(const JS::HandleValueArray& args, JS::MutableHandleObject out) { dassert(T::installType == InstallType::OverNative || T::construct != BaseInfo::construct); - out.set(_assertPtr(JS_New(_context, _proto, args))); + out.set(_assertPtr(JS_New( + _context, T::installType == InstallType::OverNative ? _constructor : _proto, args))); } void newInstance(JS::MutableHandleValue out) { @@ -324,7 +327,8 @@ public: void newInstance(const JS::HandleValueArray& args, JS::MutableHandleValue out) { dassert(T::installType == InstallType::OverNative || T::construct != BaseInfo::construct); - out.setObjectOrNull(_assertPtr(JS_New(_context, _proto, args))); + out.setObjectOrNull(_assertPtr(JS_New( + _context, T::installType == InstallType::OverNative ? _constructor : _proto, args))); } // instanceOf doesn't go up the prototype tree. It's a lower level more specific match @@ -419,7 +423,25 @@ private: if (!value.isObject()) uasserted(ErrorCodes::BadValue, "className isn't object"); - _proto.init(_context, value.toObjectOrNull()); + JS::RootedObject classNameObject(_context); + if (!JS_ValueToObject(_context, value, &classNameObject)) + throwCurrentJSException(_context, + ErrorCodes::JSInterpreterFailure, + "Couldn't convert className property into an object."); + + JS::RootedValue protoValue(_context); + if (!JS_GetPropertyById(_context, + classNameObject, + InternedStringId(_context, InternedString::prototype), + &protoValue)) + throwCurrentJSException( + _context, ErrorCodes::JSInterpreterFailure, "Couldn't get className prototype"); + + if (!protoValue.isObject()) + uasserted(ErrorCodes::BadValue, "className's prototype isn't object"); + + _constructor.init(_context, value.toObjectOrNull()); + _proto.init(_context, protoValue.toObjectOrNull()); _installFunctions(_proto, T::methods); _installFunctions(global, T::freeFunctions); @@ -508,6 +530,7 @@ private: JSContext* _context; JS::PersistentRootedObject _proto; + JS::PersistentRootedObject _constructor; JSClass _jsclass; }; diff --git a/src/mongo/scripting/utils.cpp b/src/mongo/scripting/utils.cpp index 10d8fcfe020..91bd67a5e0f 100644 --- a/src/mongo/scripting/utils.cpp +++ b/src/mongo/scripting/utils.cpp @@ -26,6 +26,7 @@ * then also delete it in the license file. */ +#include "mongo/bson/json.h" #include "mongo/scripting/engine.h" #include "mongo/util/md5.hpp" @@ -57,6 +58,20 @@ static BSONObj native_sleep(const mongo::BSONObj& args, void* data) { return b.obj(); } +static BSONObj native_tostrictjson(const mongo::BSONObj& args, void* data) { + uassert(40275, + "tostrictjson takes a single BSON object argument, and on optional boolean argument " + "for prettyPrint -- tostrictjson(obj, prettyPrint = false)", + args.nFields() >= 1 && args.firstElement().isABSONObj() && + (args.nFields() == 1 || (args.nFields() == 2 && args["1"].isBoolean()))); + + bool prettyPrint = false; + if (args.nFields() == 2) { + prettyPrint = args["1"].boolean(); + } + return BSON("" << tojson(args.firstElement().embeddedObject(), Strict, prettyPrint)); +} + // --------------------------------- // ---- installer -------- // --------------------------------- @@ -64,6 +79,7 @@ static BSONObj native_sleep(const mongo::BSONObj& args, void* data) { void installGlobalUtils(Scope& scope) { scope.injectNative("hex_md5", native_hex_md5); scope.injectNative("sleep", native_sleep); + scope.injectNative("tostrictjson", native_tostrictjson); } } // namespace mongo |