diff options
author | Matt Broadstone <mbroadst@mongodb.com> | 2022-12-16 15:45:09 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-12-16 16:22:54 +0000 |
commit | 680d92ab0437f7759e8ff36a7d8deddddab7fd0c (patch) | |
tree | 0deba681ba6398a2dab54594c5e8e431c436a443 /src/mongo/scripting/mozjs/implscope.cpp | |
parent | 6e6e6cb3daf63502666c984a78bc1961585d9f12 (diff) | |
download | mongo-680d92ab0437f7759e8ff36a7d8deddddab7fd0c.tar.gz |
SERVER-71658 Add support for dynamic import
Diffstat (limited to 'src/mongo/scripting/mozjs/implscope.cpp')
-rw-r--r-- | src/mongo/scripting/mozjs/implscope.cpp | 95 |
1 files changed, 88 insertions, 7 deletions
diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp index 1f9ef16522f..2df867c43c7 100644 --- a/src/mongo/scripting/mozjs/implscope.cpp +++ b/src/mongo/scripting/mozjs/implscope.cpp @@ -41,6 +41,7 @@ #include <js/Initialization.h> #include <js/Modules.h> #include <js/Object.h> +#include <js/Promise.h> #include <js/SourceText.h> #include <js/TypeDecls.h> #include <js/friend/ErrorMessages.h> @@ -121,6 +122,16 @@ bool closeToMaxMemory() { thread_local MozJSImplScope::ASANHandles* currentASANHandles = nullptr; +void MozJSImplScope::EnvironmentPreparer::invoke(JS::HandleObject global, Closure& closure) { + invariant(JS_IsGlobalObject(global)); + invariant(!JS_IsExceptionPending(_context)); + + JSAutoRealm ac(_context, global); + auto scope = getScope(_context); + // Log any error state in the JS context. + (void)scope->_checkErrorState(closure(_context), true, false); +} + // You may wonder what the point is to making this thread local // variable atomic. We found that without making this atomic, in // dynamic builds, the hang analyzer (GDB script) would sometimes see @@ -466,7 +477,7 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine, boost::optional<int> j _statusProto(_context), _timestampProto(_context), _uriProto(_context) { - + _environmentPreparer = std::make_unique<EnvironmentPreparer>(_context); _moduleLoader = std::make_unique<ModuleLoader>(); uassert(ErrorCodes::JSInterpreterFailure, "Failed to create ModuleLoader", _moduleLoader); uassert(ErrorCodes::JSInterpreterFailure, @@ -503,6 +514,7 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine, boost::optional<int> j } MozJSImplScope::~MozJSImplScope() { + invariant(!_promiseResult.has_value()); currentJSScope = nullptr; for (auto&& x : _funcs) { @@ -651,6 +663,66 @@ void MozJSImplScope::_MozJSCreateFunction(StringData raw, JS::MutableHandleValue uassert(10232, "not a function", fun.isObject() && js::IsFunctionObject(fun.toObjectOrNull())); } +bool MozJSImplScope::onSyncPromiseResolved(JSContext* cx, unsigned argc, JS::Value* vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + auto scope = getScope(cx); + scope->_promiseResult.emplace(args[0]); + args.rval().setUndefined(); + return true; +} + +bool MozJSImplScope::onSyncPromiseRejected(JSContext* cx, unsigned argc, JS::Value* vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + JS::HandleValue error = args.get(0); + auto scope = getScope(cx); + scope->_status = jsExceptionToStatus(cx, error, ErrorCodes::JSInterpreterFailure, ""); + return true; +} + +// Block synchronously awaiting the result of a Promise. This is okay because the test runner is +// single threaded, but we should remove this if that invariant ever changes. +bool MozJSImplScope::awaitPromise(JSContext* cx, + JS::HandleObject promise, + JS::MutableHandleValue out) { + JS::RootedObject resolved( + cx, + JS_GetFunctionObject(js::NewFunctionWithReserved( + cx, MozJSImplScope::onSyncPromiseResolved, 1, 0, "async resolved"))); + + if (!resolved) { + return false; + } + + JS::RootedObject rejected( + cx, + JS_GetFunctionObject(js::NewFunctionWithReserved( + cx, MozJSImplScope::onSyncPromiseRejected, 1, 0, "async rejected"))); + if (!rejected) { + return false; + } + + JS::AddPromiseReactions(cx, promise, resolved, rejected); + + auto scope = getScope(cx); + JS::RootedValue pOut(cx); + do { + if (scope->_checkErrorState(true)) { + break; + } + + js::RunJobs(cx); + } while (JS::GetPromiseState(promise) == JS::PromiseState::Pending); + + if (JS::GetPromiseState(promise) == JS::PromiseState::Rejected) { + return false; + } + + invariant(scope->_promiseResult.has_value()); + out.set(*scope->_promiseResult); + scope->_promiseResult = boost::none; + return true; +} + BSONObj MozJSImplScope::callThreadArgs(const BSONObj& args) { // The _runSafely() function is called for all codepaths of executing JavaScript other than // callThreadArgs(). We intentionally don't unwrap the JSInterpreterFailureWithStack error @@ -691,8 +763,18 @@ BSONObj MozJSImplScope::callThreadArgs(const BSONObj& args) { JS::RootedObject rout(_context, JS_NewPlainObject(_context)); ObjectWrapper wout(_context, rout); - wout.setValue("ret", out); + if (out.isObject()) { + JS::RootedObject maybePromise(_context, &out.toObject()); + if (JS::IsPromiseObject(maybePromise)) { + JS::RootedValue pOut(_context); + (void)_checkErrorState(awaitPromise(_context, maybePromise, &pOut), false, true); + wout.setValue("ret", pOut); + return wout.toBSON(); + } + } + + wout.setValue("ret", out); return wout.toBSON(); } @@ -826,7 +908,8 @@ bool shouldTryExecAsModule(JSContext* cx, const std::string& name, bool success) } return report->errorNumber == JSMSG_IMPORT_DECL_AT_TOP_LEVEL || - report->errorNumber == JSMSG_EXPORT_DECL_AT_TOP_LEVEL; + report->errorNumber == JSMSG_EXPORT_DECL_AT_TOP_LEVEL || + report->errorNumber == JSMSG_AWAIT_OUTSIDE_ASYNC_OR_MODULE; } bool MozJSImplScope::exec(StringData code, @@ -874,14 +957,12 @@ bool MozJSImplScope::exec(StringData code, JS::RootedScript script(_context, scriptPtr); success = JS_ExecuteScript(_context, script, &out); } else { - JS::Rooted<JS::Value> returnValue(_context); JS::RootedObject module(_context, modulePtr); - success = JS::ModuleInstantiate(_context, module); if (success) { - success = JS::ModuleEvaluate(_context, module, &returnValue); + success = JS::ModuleEvaluate(_context, module, &out); if (success) { - JS::RootedObject evaluationPromise(_context, &returnValue.toObject()); + JS::RootedObject evaluationPromise(_context, &out.toObject()); success = JS::ThrowOnModuleEvaluationFailure(_context, evaluationPromise); } } |