diff options
Diffstat (limited to 'src/mongo/scripting')
| -rw-r--r-- | src/mongo/scripting/deadline_monitor.cpp | 2 | ||||
| -rw-r--r-- | src/mongo/scripting/deadline_monitor.h | 6 | ||||
| -rw-r--r-- | src/mongo/scripting/deadline_monitor_test.cpp | 13 | ||||
| -rw-r--r-- | src/mongo/scripting/engine.cpp | 16 | ||||
| -rw-r--r-- | src/mongo/scripting/engine.h | 4 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/PosixNSPR.cpp | 13 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/bson.cpp | 10 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/db.cpp | 53 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/db.h | 5 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/dbcollection.cpp | 10 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/dbcollection.h | 5 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/dbquery.cpp | 25 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/dbquery.h | 6 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/implscope.cpp | 195 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/implscope.h | 8 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/mongohelpers.js | 8 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/proxyscope.cpp | 4 | ||||
| -rw-r--r-- | src/mongo/scripting/mozjs/proxyscope.h | 2 |
18 files changed, 228 insertions, 157 deletions
diff --git a/src/mongo/scripting/deadline_monitor.cpp b/src/mongo/scripting/deadline_monitor.cpp index f75c34a0cc2..5eb0f52e5de 100644 --- a/src/mongo/scripting/deadline_monitor.cpp +++ b/src/mongo/scripting/deadline_monitor.cpp @@ -34,7 +34,7 @@ namespace mongo { -MONGO_EXPORT_SERVER_PARAMETER(scriptingEngineInterruptIntervalMS, int, 0); +MONGO_EXPORT_SERVER_PARAMETER(scriptingEngineInterruptIntervalMS, int, 1000); int getScriptingEngineInterruptInterval() { return scriptingEngineInterruptIntervalMS.load(); diff --git a/src/mongo/scripting/deadline_monitor.h b/src/mongo/scripting/deadline_monitor.h index b3c5007c72d..9081d7366e6 100644 --- a/src/mongo/scripting/deadline_monitor.h +++ b/src/mongo/scripting/deadline_monitor.h @@ -141,10 +141,10 @@ private: const Date_t now = Date_t::now(); const auto interruptInterval = Milliseconds{getScriptingEngineInterruptInterval()}; - if ((interruptInterval.count() > 0) && (now - lastInterruptCycle > interruptInterval)) { + if (now - lastInterruptCycle > interruptInterval) { for (const auto& task : _tasks) { - if (task.second > now) - task.first->interrupt(); + if (task.first->isKillPending()) + task.first->kill(); } lastInterruptCycle = now; } diff --git a/src/mongo/scripting/deadline_monitor_test.cpp b/src/mongo/scripting/deadline_monitor_test.cpp index 71daefbce8f..d802673bd3d 100644 --- a/src/mongo/scripting/deadline_monitor_test.cpp +++ b/src/mongo/scripting/deadline_monitor_test.cpp @@ -73,8 +73,12 @@ public: _group->noteKill(); } void interrupt() {} + const bool isKillPending() { + return killPending; + } TaskGroup* _group; uint64_t _killed; + bool killPending = false; }; // single task expires before stopping the deadline @@ -175,4 +179,13 @@ TEST(DeadlineMonitor, MultipleTasksExpireOrComplete) { } } +TEST(DeadlineMonitor, IsKillPendingKills) { + DeadlineMonitor<Task> dm; + TaskGroup group; + Task task(&group); + dm.startDeadline(&task, -1); + task.killPending = true; + group.waitForKillCount(1); + ASSERT(task._killed); +} } // namespace mongo diff --git a/src/mongo/scripting/engine.cpp b/src/mongo/scripting/engine.cpp index 3e9a5c542cf..1848bccfba2 100644 --- a/src/mongo/scripting/engine.cpp +++ b/src/mongo/scripting/engine.cpp @@ -42,6 +42,7 @@ #include "mongo/db/service_context.h" #include "mongo/platform/unordered_set.h" #include "mongo/scripting/dbdirectclient_factory.h" +#include "mongo/util/fail_point_service.h" #include "mongo/util/file.h" #include "mongo/util/log.h" #include "mongo/util/text.h" @@ -55,7 +56,10 @@ using std::unique_ptr; AtomicInt64 Scope::_lastVersion(1); + namespace { + +MONGO_FP_DECLARE(mr_killop_test_fp); // 2 GB is the largest support Javascript file size. const fileofs kMaxJsFileLength = fileofs(2) * 1024 * 1024 * 1024; @@ -231,6 +235,15 @@ void Scope::loadStored(OperationContext* txn, bool ignoreNotConnected) { uassert(10209, str::stream() << "name has to be a string: " << n, n.type() == String); uassert(10210, "value has to be set", v.type() != EOO); + if (MONGO_FAIL_POINT(mr_killop_test_fp)) { + + /* This thread sleep makes the interrupts in the test come in at a time + * where the js misses the interrupt and throw an exception instead of + * being interrupted + */ + stdx::this_thread::sleep_for(stdx::chrono::seconds(1)); + } + try { setElement(n.valuestr(), v, o); thisTime.insert(n.valuestr()); @@ -427,6 +440,9 @@ public: void advanceGeneration() { _real->advanceGeneration(); } + void requireOwnedObjects() override { + _real->requireOwnedObjects(); + } bool isKillPending() const { return _real->isKillPending(); } diff --git a/src/mongo/scripting/engine.h b/src/mongo/scripting/engine.h index 2b2ab0ff14d..e773dec5f60 100644 --- a/src/mongo/scripting/engine.h +++ b/src/mongo/scripting/engine.h @@ -44,7 +44,7 @@ class OperationContext; struct JSFile { const char* name; - const StringData& source; + const StringData source; }; class Scope { @@ -103,6 +103,8 @@ public: virtual void advanceGeneration() = 0; + virtual void requireOwnedObjects() = 0; + virtual ScriptingFunction createFunction(const char* code); /** diff --git a/src/mongo/scripting/mozjs/PosixNSPR.cpp b/src/mongo/scripting/mozjs/PosixNSPR.cpp index ed1a3d5a49d..9054e930443 100644 --- a/src/mongo/scripting/mozjs/PosixNSPR.cpp +++ b/src/mongo/scripting/mozjs/PosixNSPR.cpp @@ -24,6 +24,7 @@ #include "mongo/stdx/chrono.h" #include "mongo/stdx/condition_variable.h" +#include "mongo/stdx/memory.h" #include "mongo/stdx/mutex.h" #include "mongo/stdx/thread.h" #include "mongo/util/concurrency/thread_name.h" @@ -98,9 +99,13 @@ PRThread* PR_CreateThread(PRThreadType type, MOZ_ASSERT(priority == PR_PRIORITY_NORMAL); try { - std::unique_ptr<nspr::Thread, void (*)(nspr::Thread*)> t( - js_new<nspr::Thread>(start, arg, state != PR_UNJOINABLE_THREAD), - js_delete_nonconst<nspr::Thread>); + // We can't use the nspr allocator to allocate this thread, because under asan we + // instrument the allocator so that asan can track the pointers correctly. This + // instrumentation + // requires that pointers be deleted in the same thread that they were allocated in. + // The threads created in PR_CreateThread are not always freed in the same thread + // that they were created in. So, we use the standard allocator here. + auto t = mongo::stdx::make_unique<nspr::Thread>(start, arg, state != PR_UNJOINABLE_THREAD); t->thread() = mongo::stdx::thread(&nspr::Thread::ThreadRoutine, t.get()); @@ -118,7 +123,7 @@ PRStatus PR_JoinThread(PRThread* thread) { try { thread->thread().join(); - js_delete(thread); + delete thread; return PR_SUCCESS; } catch (...) { diff --git a/src/mongo/scripting/mozjs/bson.cpp b/src/mongo/scripting/mozjs/bson.cpp index 5a2ebd0dfed..a2881c44664 100644 --- a/src/mongo/scripting/mozjs/bson.cpp +++ b/src/mongo/scripting/mozjs/bson.cpp @@ -59,13 +59,17 @@ namespace { * the appearance of mutable state on the read/write versions. */ struct BSONHolder { - BSONHolder(const BSONObj& obj, const BSONObj* parent, std::size_t generation, bool ro) + BSONHolder(const BSONObj& obj, const BSONObj* parent, const MozJSImplScope* scope, bool ro) : _obj(obj), - _generation(generation), + _generation(scope->getGeneration()), _isOwned(obj.isOwned() || (parent && parent->isOwned())), _resolved(false), _readOnly(ro), _altered(false) { + uassert( + ErrorCodes::BadValue, + "Attempt to bind an unowned BSON Object to a JS scope marked as requiring ownership", + _isOwned || (!scope->requiresOwnedObjects())); if (parent) { _parent.emplace(*parent); } @@ -107,7 +111,7 @@ void BSONInfo::make( auto scope = getScope(cx); scope->getProto<BSONInfo>().newObject(obj); - JS_SetPrivate(obj, scope->trackedNew<BSONHolder>(bson, parent, scope->getGeneration(), ro)); + JS_SetPrivate(obj, scope->trackedNew<BSONHolder>(bson, parent, scope, ro)); } void BSONInfo::finalize(JSFreeOp* fop, JSObject* obj) { diff --git a/src/mongo/scripting/mozjs/db.cpp b/src/mongo/scripting/mozjs/db.cpp index 7fa2f179241..7aa73337237 100644 --- a/src/mongo/scripting/mozjs/db.cpp +++ b/src/mongo/scripting/mozjs/db.cpp @@ -45,38 +45,17 @@ namespace mozjs { const char* const DBInfo::className = "DB"; -void DBInfo::getProperty(JSContext* cx, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue vp) { - // 2nd look into real values, may be cached collection object - if (!vp.isUndefined()) { - auto scope = getScope(cx); - auto opContext = scope->getOpContext(); - - if (opContext && vp.isObject()) { - ObjectWrapper o(cx, vp); - - if (o.hasOwnField(InternedString::_fullName)) { - // need to check every time that the collection did not get sharded - if (haveLocalShardingInfo(opContext, o.getString(InternedString::_fullName))) - uasserted(ErrorCodes::BadValue, "can't use sharded collection from db.eval"); - } - } +void DBInfo::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp) { + *resolvedp = false; - return; - } + JS::RootedValue coll(cx); JS::RootedObject parent(cx); if (!JS_GetPrototype(cx, obj, &parent)) uasserted(ErrorCodes::JSInterpreterFailure, "Couldn't get prototype"); ObjectWrapper parentWrapper(cx, parent); - - if (parentWrapper.hasOwnField(id)) { - parentWrapper.getValue(id, vp); - return; - } + ObjectWrapper o(cx, obj); IdWrapper idw(cx, id); @@ -87,21 +66,39 @@ void DBInfo::getProperty(JSContext* cx, if (sname.size() == 0 || sname[0] == '_') { return; } + + // SpiderMonkey will call resolve even for __proto__ so acknowledge it exists. + if (sname == "__proto__"_sd) { + *resolvedp = true; + return; + } + } + + // Check if this exists on the parent, ie. DBCollection::resolve case + if (parentWrapper.hasOwnField(id)) { + parentWrapper.getValue(id, &coll); + + o.defineProperty(id, coll, 0); + + *resolvedp = true; + return; } // no hit, create new collection JS::RootedValue getCollection(cx); parentWrapper.getValue(InternedString::getCollection, &getCollection); + // Check if getCollection has been installed yet + // It is undefined if the user has a db name the same as one of methods/properties of the DB + // object. if (!(getCollection.isObject() && JS_ObjectIsFunction(cx, getCollection.toObjectOrNull()))) { - uasserted(ErrorCodes::BadValue, "getCollection is not a function"); + return; } JS::AutoValueArray<1> args(cx); idw.toValue(args[0]); - JS::RootedValue coll(cx); ObjectWrapper(cx, obj).callMethod(getCollection, args, &coll); uassert(16861, @@ -111,7 +108,7 @@ void DBInfo::getProperty(JSContext* cx, // cache collection for reuse, don't enumerate ObjectWrapper(cx, obj).defineProperty(id, coll, 0); - vp.set(coll); + *resolvedp = true; } void DBInfo::construct(JSContext* cx, JS::CallArgs args) { diff --git a/src/mongo/scripting/mozjs/db.h b/src/mongo/scripting/mozjs/db.h index c752953236f..8f080265ef0 100644 --- a/src/mongo/scripting/mozjs/db.h +++ b/src/mongo/scripting/mozjs/db.h @@ -44,10 +44,7 @@ namespace mozjs { */ struct DBInfo : public BaseInfo { static void construct(JSContext* cx, JS::CallArgs args); - static void getProperty(JSContext* cx, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue vp); + static void resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp); static const char* const className; }; diff --git a/src/mongo/scripting/mozjs/dbcollection.cpp b/src/mongo/scripting/mozjs/dbcollection.cpp index e0f903dd1fc..b1983bd367c 100644 --- a/src/mongo/scripting/mozjs/dbcollection.cpp +++ b/src/mongo/scripting/mozjs/dbcollection.cpp @@ -44,11 +44,11 @@ namespace mozjs { const char* const DBCollectionInfo::className = "DBCollection"; -void DBCollectionInfo::getProperty(JSContext* cx, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue vp) { - DBInfo::getProperty(cx, obj, id, vp); +void DBCollectionInfo::resolve(JSContext* cx, + JS::HandleObject obj, + JS::HandleId id, + bool* resolvedp) { + DBInfo::resolve(cx, obj, id, resolvedp); } void DBCollectionInfo::construct(JSContext* cx, JS::CallArgs args) { diff --git a/src/mongo/scripting/mozjs/dbcollection.h b/src/mongo/scripting/mozjs/dbcollection.h index 34be422dfd4..ed3c39ff0f2 100644 --- a/src/mongo/scripting/mozjs/dbcollection.h +++ b/src/mongo/scripting/mozjs/dbcollection.h @@ -44,10 +44,7 @@ namespace mozjs { */ struct DBCollectionInfo : public BaseInfo { static void construct(JSContext* cx, JS::CallArgs args); - static void getProperty(JSContext* cx, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue vp); + static void resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp); static const char* const className; }; diff --git a/src/mongo/scripting/mozjs/dbquery.cpp b/src/mongo/scripting/mozjs/dbquery.cpp index c2543814505..15bd45bde3c 100644 --- a/src/mongo/scripting/mozjs/dbquery.cpp +++ b/src/mongo/scripting/mozjs/dbquery.cpp @@ -105,13 +105,8 @@ void DBQueryInfo::construct(JSContext* cx, JS::CallArgs args) { args.rval().setObjectOrNull(thisv); } -void DBQueryInfo::getProperty(JSContext* cx, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue vp) { - if (!vp.isUndefined()) { - return; - } +void DBQueryInfo::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp) { + *resolvedp = false; IdWrapper wid(cx, id); @@ -134,9 +129,19 @@ void DBQueryInfo::getProperty(JSContext* cx, args[0].setInt32(wid.toInt32()); - ObjectWrapper(cx, obj).callMethod(arrayAccess, args, vp); - } else { - uasserted(ErrorCodes::BadValue, "arrayAccess is not a function"); + JS::RootedValue vp(cx); + + ObjectWrapper(cx, obj).callMethod(arrayAccess, args, &vp); + + if (!vp.isNullOrUndefined()) { + ObjectWrapper o(cx, obj); + + // Assumes the user won't modify the contents of what DBQuery::arrayAccess returns + // otherwise we need to install a getter. + o.defineProperty(id, vp, 0); + } + + *resolvedp = true; } } diff --git a/src/mongo/scripting/mozjs/dbquery.h b/src/mongo/scripting/mozjs/dbquery.h index dc844c3084c..590185c55ee 100644 --- a/src/mongo/scripting/mozjs/dbquery.h +++ b/src/mongo/scripting/mozjs/dbquery.h @@ -41,10 +41,8 @@ namespace mozjs { */ struct DBQueryInfo : public BaseInfo { static void construct(JSContext* cx, JS::CallArgs args); - static void getProperty(JSContext* cx, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue vp); + static void resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp); + static const char* const className; }; diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp index c37ebc0cb2a..c31d5b940a4 100644 --- a/src/mongo/scripting/mozjs/implscope.cpp +++ b/src/mongo/scripting/mozjs/implscope.cpp @@ -258,7 +258,6 @@ MozJSImplScope::ASANHandles::ASANHandles() { } MozJSImplScope::ASANHandles::~ASANHandles() { - invariant(_handles.empty()); invariant(kCurrentASANHandles == this); kCurrentASANHandles = nullptr; } @@ -342,6 +341,14 @@ MozJSImplScope::MozRuntime::MozRuntime(const MozJSScriptEngine* engine) { .setIon(true) .setAsyncStack(false) .setNativeRegExp(true); + } else { + JS::RuntimeOptionsRef(_runtime.get()) + .setAsmJS(false) + .setThrowOnAsmJSValidationFailure(false) + .setBaseline(false) + .setIon(false) + .setAsyncStack(false) + .setNativeRegExp(false); } const StackLocator locator; @@ -403,6 +410,7 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine) _connectState(ConnectState::Not), _status(Status::OK()), _generation(0), + _requireOwnedObjects(false), _hasOutOfMemoryException(false), _binDataProto(_context), _bsonProto(_context), @@ -491,82 +499,81 @@ void MozJSImplScope::init(const BSONObj* data) { } } -void MozJSImplScope::setNumber(const char* field, double val) { - MozJSEntry entry(this); +template <typename ImplScopeFunction> +auto MozJSImplScope::_runSafely(ImplScopeFunction&& functionToRun) -> decltype(functionToRun()) { + try { + MozJSEntry entry(this); + return functionToRun(); + } catch (...) { + _error = _status.reason(); - ObjectWrapper(_context, _global).setNumber(field, val); + // Clear the status state + auto status = std::move(_status); + uassertStatusOK(status); + throw; + } } -void MozJSImplScope::setString(const char* field, StringData val) { - MozJSEntry entry(this); +void MozJSImplScope::setNumber(const char* field, double val) { + _runSafely([this, &field, &val] { ObjectWrapper(_context, _global).setNumber(field, val); }); +} - ObjectWrapper(_context, _global).setString(field, val); +void MozJSImplScope::setString(const char* field, StringData val) { + _runSafely([this, &field, &val] { ObjectWrapper(_context, _global).setString(field, val); }); } void MozJSImplScope::setBoolean(const char* field, bool val) { - MozJSEntry entry(this); - - ObjectWrapper(_context, _global).setBoolean(field, val); + _runSafely([this, &field, &val] { ObjectWrapper(_context, _global).setBoolean(field, val); }); } void MozJSImplScope::setElement(const char* field, const BSONElement& e, const BSONObj& parent) { - MozJSEntry entry(this); + _runSafely([this, &field, &e, &parent] { - ObjectWrapper(_context, _global).setBSONElement(field, e, parent, false); + ObjectWrapper(_context, _global).setBSONElement(field, e, parent, false); + }); } void MozJSImplScope::setObject(const char* field, const BSONObj& obj, bool readOnly) { - MozJSEntry entry(this); + _runSafely([this, &field, &obj, &readOnly] { - ObjectWrapper(_context, _global).setBSON(field, obj, readOnly); + ObjectWrapper(_context, _global).setBSON(field, obj, readOnly); + }); } int MozJSImplScope::type(const char* field) { - MozJSEntry entry(this); - - return ObjectWrapper(_context, _global).type(field); + return _runSafely([this, &field] { return ObjectWrapper(_context, _global).type(field); }); } double MozJSImplScope::getNumber(const char* field) { - MozJSEntry entry(this); - - return ObjectWrapper(_context, _global).getNumber(field); + return _runSafely([this, &field] { return ObjectWrapper(_context, _global).getNumber(field); }); } int MozJSImplScope::getNumberInt(const char* field) { - MozJSEntry entry(this); - - return ObjectWrapper(_context, _global).getNumberInt(field); + return _runSafely( + [this, &field] { return ObjectWrapper(_context, _global).getNumberInt(field); }); } long long MozJSImplScope::getNumberLongLong(const char* field) { - MozJSEntry entry(this); - - return ObjectWrapper(_context, _global).getNumberLongLong(field); + return _runSafely( + [this, &field] { return ObjectWrapper(_context, _global).getNumberLongLong(field); }); } Decimal128 MozJSImplScope::getNumberDecimal(const char* field) { - MozJSEntry entry(this); - - return ObjectWrapper(_context, _global).getNumberDecimal(field); + return _runSafely( + [this, &field] { return ObjectWrapper(_context, _global).getNumberDecimal(field); }); } std::string MozJSImplScope::getString(const char* field) { - MozJSEntry entry(this); - - return ObjectWrapper(_context, _global).getString(field); + return _runSafely([this, &field] { return ObjectWrapper(_context, _global).getString(field); }); } bool MozJSImplScope::getBoolean(const char* field) { - MozJSEntry entry(this); - - return ObjectWrapper(_context, _global).getBoolean(field); + return _runSafely( + [this, &field] { return ObjectWrapper(_context, _global).getBoolean(field); }); } BSONObj MozJSImplScope::getObject(const char* field) { - MozJSEntry entry(this); - - return ObjectWrapper(_context, _global).getObject(field); + return _runSafely([this, &field] { return ObjectWrapper(_context, _global).getObject(field); }); } void MozJSImplScope::newFunction(StringData raw, JS::MutableHandleValue out) { @@ -642,17 +649,15 @@ ScriptingFunction MozJSImplScope::_createFunction(const char* raw) { } void MozJSImplScope::setFunction(const char* field, const char* code) { - MozJSEntry entry(this); - - JS::RootedValue fun(_context); - _MozJSCreateFunction(code, &fun); - ObjectWrapper(_context, _global).setValue(field, fun); + _runSafely([this, &field, &code] { + JS::RootedValue fun(_context); + _MozJSCreateFunction(code, &fun); + ObjectWrapper(_context, _global).setValue(field, fun); + }); } void MozJSImplScope::rename(const char* from, const char* to) { - MozJSEntry entry(this); - - ObjectWrapper(_context, _global).rename(from, to); + _runSafely([this, &from, &to] { ObjectWrapper(_context, _global).rename(from, to); }); } int MozJSImplScope::invoke(ScriptingFunction func, @@ -764,15 +769,15 @@ bool MozJSImplScope::exec(StringData code, } void MozJSImplScope::injectNative(const char* field, NativeFunction func, void* data) { - MozJSEntry entry(this); - - JS::RootedObject obj(_context); + _runSafely([this, &field, &func, &data] { + JS::RootedObject obj(_context); - NativeFunctionInfo::make(_context, &obj, func, data); + NativeFunctionInfo::make(_context, &obj, func, data); - JS::RootedValue value(_context); - value.setObjectOrNull(obj); - ObjectWrapper(_context, _global).setValue(field, value); + JS::RootedValue value(_context); + value.setObjectOrNull(obj); + ObjectWrapper(_context, _global).setValue(field, value); + }); } void MozJSImplScope::gc() { @@ -781,63 +786,65 @@ void MozJSImplScope::gc() { } void MozJSImplScope::localConnectForDbEval(OperationContext* txn, const char* dbName) { - MozJSEntry entry(this); - - if (_connectState == ConnectState::External) - uasserted(12510, "externalSetup already called, can't call localConnect"); - if (_connectState == ConnectState::Local) { - if (_localDBName == dbName) - return; - uasserted(12511, - str::stream() << "localConnect previously called with name " << _localDBName); - } + _runSafely([this, &txn, &dbName] { + if (_connectState == ConnectState::External) + uasserted(12510, "externalSetup already called, can't call localConnect"); + if (_connectState == ConnectState::Local) { + if (_localDBName == dbName) + return; + uasserted(12511, + str::stream() << "localConnect previously called with name " << _localDBName); + } - // NOTE: order is important here. the following methods must be called after - // the above conditional statements. + // NOTE: order is important here. the following methods must be called after + // the above conditional statements. - _connectState = ConnectState::Local; - _localDBName = dbName; + _connectState = ConnectState::Local; + _localDBName = dbName; - loadStored(txn); + loadStored(txn); - // install db access functions in the global object - installDBAccess(); + // install db access functions in the global object + installDBAccess(); - // install the Mongo function object and instantiate the 'db' global - _mongoLocalProto.install(_global); - execCoreFiles(); + // install the Mongo function object and instantiate the 'db' global + _mongoLocalProto.install(_global); + execCoreFiles(); - const char* const makeMongo = "const _mongo = new Mongo()"; - exec(makeMongo, "local connect 2", false, true, true, 0); + const char* const makeMongo = "const _mongo = new Mongo()"; + exec(makeMongo, "local connect 2", false, true, true, 0); - std::string makeDB = str::stream() << "const db = _mongo.getDB(\"" << dbName << "\");"; - exec(makeDB, "local connect 3", false, true, true, 0); + std::string makeDB = str::stream() << "const db = _mongo.getDB(\"" << dbName << "\");"; + exec(makeDB, "local connect 3", false, true, true, 0); + }); } void MozJSImplScope::externalSetup() { - MozJSEntry entry(this); - if (_connectState == ConnectState::External) - return; - if (_connectState == ConnectState::Local) - uasserted(12512, "localConnect already called, can't call externalSetup"); + _runSafely([&] { + if (_connectState == ConnectState::External) + return; + if (_connectState == ConnectState::Local) + uasserted(12512, "localConnect already called, can't call externalSetup"); - // install db access functions in the global object - installDBAccess(); + // install db access functions in the global object + installDBAccess(); - // install thread-related functions (e.g. _threadInject) - installFork(); + // install thread-related functions (e.g. _threadInject) + installFork(); - // install the Mongo function object - _mongoExternalProto.install(_global); - execCoreFiles(); - _connectState = ConnectState::External; + // install the Mongo function object + _mongoExternalProto.install(_global); + execCoreFiles(); + _connectState = ConnectState::External; + }); } void MozJSImplScope::reset() { unregisterOperation(); _pendingKill.store(false); _pendingGC.store(false); + _requireOwnedObjects = false; advanceGeneration(); } @@ -951,6 +958,14 @@ void MozJSImplScope::advanceGeneration() { _generation++; } +void MozJSImplScope::requireOwnedObjects() { + _requireOwnedObjects = true; +} + +bool MozJSImplScope::requiresOwnedObjects() const { + return _requireOwnedObjects; +} + const std::string& MozJSImplScope::getParentStack() const { return _parentStack; } diff --git a/src/mongo/scripting/mozjs/implscope.h b/src/mongo/scripting/mozjs/implscope.h index df37553e181..2de309e87ab 100644 --- a/src/mongo/scripting/mozjs/implscope.h +++ b/src/mongo/scripting/mozjs/implscope.h @@ -309,6 +309,10 @@ public: void advanceGeneration() override; + void requireOwnedObjects() override; + + bool requiresOwnedObjects() const; + JS::HandleId getInternedStringId(InternedString name) { return _internedStrings.getInternedString(name); } @@ -341,6 +345,9 @@ public: }; private: + template <typename ImplScopeFunction> + auto _runSafely(ImplScopeFunction&& functionToRun) -> decltype(functionToRun()); + void _MozJSCreateFunction(StringData raw, JS::MutableHandleValue fun); /** @@ -402,6 +409,7 @@ private: Status _status; std::string _parentStack; std::size_t _generation; + bool _requireOwnedObjects; bool _hasOutOfMemoryException; WrapType<BinDataInfo> _binDataProto; diff --git a/src/mongo/scripting/mozjs/mongohelpers.js b/src/mongo/scripting/mozjs/mongohelpers.js index b0b35bb2fe8..d3f743623a3 100644 --- a/src/mongo/scripting/mozjs/mongohelpers.js +++ b/src/mongo/scripting/mozjs/mongohelpers.js @@ -33,6 +33,14 @@ exportToMongoHelpers = { // This function accepts an expression or function body and returns a function definition 'functionExpressionParser': function functionExpressionParser(fnSrc) { + + // Ensure that a provided expression or function body is not terminated with a ';'. + // This ensures we interpret the input as a single expression, rather than a sequence + // of expressions, and can wrap it in parentheses. + while (fnSrc.endsWith(";") || fnSrc != fnSrc.trimRight()) { + fnSrc = fnSrc.slice(0, -1).trimRight(); + } + var parseTree; try { parseTree = this.Reflect.parse(fnSrc); diff --git a/src/mongo/scripting/mozjs/proxyscope.cpp b/src/mongo/scripting/mozjs/proxyscope.cpp index 6a6d20f4448..05697468f07 100644 --- a/src/mongo/scripting/mozjs/proxyscope.cpp +++ b/src/mongo/scripting/mozjs/proxyscope.cpp @@ -122,6 +122,10 @@ void MozJSProxyScope::advanceGeneration() { run([&] { _implScope->advanceGeneration(); }); } +void MozJSProxyScope::requireOwnedObjects() { + run([&] { _implScope->requireOwnedObjects(); }); +} + double MozJSProxyScope::getNumber(const char* field) { double out; run([&] { out = _implScope->getNumber(field); }); diff --git a/src/mongo/scripting/mozjs/proxyscope.h b/src/mongo/scripting/mozjs/proxyscope.h index 451981330a1..4dd69a3ebe9 100644 --- a/src/mongo/scripting/mozjs/proxyscope.h +++ b/src/mongo/scripting/mozjs/proxyscope.h @@ -129,6 +129,8 @@ public: void advanceGeneration() override; + void requireOwnedObjects() override; + double getNumber(const char* field) override; int getNumberInt(const char* field) override; long long getNumberLongLong(const char* field) override; |
