summaryrefslogtreecommitdiff
path: root/src/mongo/scripting
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/scripting')
-rw-r--r--src/mongo/scripting/deadline_monitor.cpp2
-rw-r--r--src/mongo/scripting/deadline_monitor.h6
-rw-r--r--src/mongo/scripting/deadline_monitor_test.cpp13
-rw-r--r--src/mongo/scripting/engine.cpp16
-rw-r--r--src/mongo/scripting/engine.h4
-rw-r--r--src/mongo/scripting/mozjs/PosixNSPR.cpp13
-rw-r--r--src/mongo/scripting/mozjs/bson.cpp10
-rw-r--r--src/mongo/scripting/mozjs/db.cpp53
-rw-r--r--src/mongo/scripting/mozjs/db.h5
-rw-r--r--src/mongo/scripting/mozjs/dbcollection.cpp10
-rw-r--r--src/mongo/scripting/mozjs/dbcollection.h5
-rw-r--r--src/mongo/scripting/mozjs/dbquery.cpp25
-rw-r--r--src/mongo/scripting/mozjs/dbquery.h6
-rw-r--r--src/mongo/scripting/mozjs/implscope.cpp195
-rw-r--r--src/mongo/scripting/mozjs/implscope.h8
-rw-r--r--src/mongo/scripting/mozjs/mongohelpers.js8
-rw-r--r--src/mongo/scripting/mozjs/proxyscope.cpp4
-rw-r--r--src/mongo/scripting/mozjs/proxyscope.h2
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;