diff options
Diffstat (limited to 'src/mongo/scripting/mozjs/implscope.cpp')
-rw-r--r-- | src/mongo/scripting/mozjs/implscope.cpp | 134 |
1 files changed, 97 insertions, 37 deletions
diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp index 9f201cc3dc7..830d2df320a 100644 --- a/src/mongo/scripting/mozjs/implscope.cpp +++ b/src/mongo/scripting/mozjs/implscope.cpp @@ -121,6 +121,63 @@ struct MozJSImplScope::MozJSEntry { MozJSImplScope* _scope; }; +void MozJSImplScope::_reportError(JSContext* cx, const char* message, JSErrorReport* report) { + auto scope = getScope(cx); + + // If we are recursively calling _reportError because of ReportOverRecursed, lets just quit now + if (scope->_inReportError) { + return; + } + + scope->_inReportError = true; + const auto guard = makeGuard([&] { scope->_inReportError = false; }); + + if (!JSREPORT_IS_WARNING(report->flags)) { + + std::string exceptionMsg; + + try { + // TODO: something far more elaborate that mimics the stack printing from v8 + JS::RootedValue excn(cx); + if (JS_GetPendingException(cx, &excn) && excn.isObject()) { + JS::RootedValue stack(cx); + + JS::RootedObject obj(cx, excn.toObjectOrNull()); + ObjectWrapper o(cx, obj); + o.getValue("stack", &stack); + + auto stackStr = ValueWriter(cx, stack).toString(); + + if (stackStr.empty()) { + // The JavaScript Error objects resulting from C++ exceptions may not always + // have a non-empty "stack" property. We instead use the line and column + // numbers of where in the JavaScript code the C++ function was called from. + str::stream ss; + ss << "@" << report->filename << ":" << report->lineno << ":" << report->column + << "\n"; + stackStr = ss; + } + + auto status = + jsExceptionToStatus(cx, excn, ErrorCodes::JSInterpreterFailure, message) + .withReason(message); + scope->_status = {JSExceptionInfo(std::move(stackStr), std::move(status)), message}; + + return; + } + + exceptionMsg = message; + } catch (const DBException& dbe) { + exceptionMsg = "Unknown error occured while processing exception"; + log() << exceptionMsg << ":" << dbe.toString() << ":" << message; + } + + scope->_status = + JSErrorReportToStatus(cx, report, ErrorCodes::JSInterpreterFailure, message) + .withReason(exceptionMsg); + } +} + std::string MozJSImplScope::getError() { return ""; } @@ -162,11 +219,11 @@ void MozJSImplScope::kill() { } } _sleepCondition.notify_all(); - JS_RequestInterruptCallback(_context); + JS_RequestInterruptCallback(_runtime); } void MozJSImplScope::interrupt() { - JS_RequestInterruptCallback(_context); + JS_RequestInterruptCallback(_runtime); } bool MozJSImplScope::isKillPending() const { @@ -185,12 +242,12 @@ bool MozJSImplScope::isJavaScriptProtectionEnabled() const { bool MozJSImplScope::_interruptCallback(JSContext* cx) { auto scope = getScope(cx); - JS_DisableInterruptCallback(scope->_context); - auto guard = makeGuard([&]() { JS_ResetInterruptCallback(scope->_context, false); }); + JS_SetInterruptCallback(scope->_runtime, nullptr); + auto guard = makeGuard([&]() { JS_SetInterruptCallback(scope->_runtime, _interruptCallback); }); if (scope->_pendingGC.load() || closeToMaxMemory()) { scope->_pendingGC.store(false); - JS_GC(scope->_context); + JS_GC(scope->_runtime); } else { JS_MaybeGC(cx); } @@ -216,7 +273,7 @@ bool MozJSImplScope::_interruptCallback(JSContext* cx) { return scope->_status.isOK(); } -void MozJSImplScope::_gcCallback(JSContext* rt, JSGCStatus status, void* data) { +void MozJSImplScope::_gcCallback(JSRuntime* rt, JSGCStatus status, void* data) { if (!shouldLog(logger::LogSeverity::Debug(1))) { // don't collect stats unless verbose return; @@ -303,14 +360,13 @@ MozJSImplScope::MozRuntime::MozRuntime(const MozJSScriptEngine* engine) { gFirstRuntimeCreated = true; } - _context = std::unique_ptr<JSContext, std::function<void(JSContext*)>>( - JS_NewContext(kMaxBytesBeforeGC, JS::DefaultNurseryBytes), - [](JSContext* ptr) { JS_DestroyContext(ptr); }); - uassert(ErrorCodes::JSInterpreterFailure, "Failed to initialize JSContext", _context); + _runtime = std::unique_ptr<JSRuntime, std::function<void(JSRuntime*)>>( + JS_NewRuntime(kMaxBytesBeforeGC), [](JSRuntime* ptr) { JS_DestroyRuntime(ptr); }); + uassert(ErrorCodes::JSInterpreterFailure, "Failed to initialize JSRuntime", _runtime); // We turn on a variety of optimizations if the jit is enabled if (engine->isJITEnabled()) { - JS::ContextOptionsRef(_context.get()) + JS::RuntimeOptionsRef(_runtime.get()) .setAsmJS(true) .setThrowOnAsmJSValidationFailure(true) .setBaseline(true) @@ -318,7 +374,7 @@ MozJSImplScope::MozRuntime::MozRuntime(const MozJSScriptEngine* engine) { .setAsyncStack(false) .setNativeRegExp(true); } else { - JS::ContextOptionsRef(_context.get()) + JS::RuntimeOptionsRef(_runtime.get()) .setAsmJS(false) .setThrowOnAsmJSValidationFailure(false) .setBaseline(false) @@ -327,14 +383,6 @@ MozJSImplScope::MozRuntime::MozRuntime(const MozJSScriptEngine* engine) { .setNativeRegExp(false); } - uassert(ErrorCodes::JSInterpreterFailure, - "InitSelfHostedCode", - JS::InitSelfHostedCode(_context.get())); - - uassert(ErrorCodes::ExceededMemoryLimit, - "Out of memory while trying to initialize javascript scope", - mallocMemoryLimit == 0 || mongo::sm::get_total_bytes() < mallocMemoryLimit); - const StackLocator locator; const auto available = locator.available(); if (available) { @@ -364,19 +412,26 @@ MozJSImplScope::MozRuntime::MozRuntime(const MozJSScriptEngine* engine) { const decltype(available_stack_space) reserve_stack_space = 64 * 1024; #endif - JS_SetNativeStackQuota(_context.get(), available_stack_space - reserve_stack_space); + JS_SetNativeStackQuota(_runtime.get(), available_stack_space - reserve_stack_space); } // The memory limit is in megabytes - JS_SetGCParametersBasedOnAvailableMemory(_context.get(), engine->getJSHeapLimitMB()); - // TODO SERVER-39152 apply the mozilla patch to stop overriding JSGC_MAX_BYTES - JS_SetGCParameter(_context.get(), JSGC_MAX_BYTES, 0xffffffff); + JS_SetGCParametersBasedOnAvailableMemory(_runtime.get(), engine->getJSHeapLimitMB()); } + + _context = std::unique_ptr<JSContext, std::function<void(JSContext*)>>( + JS_NewContext(_runtime.get(), kStackChunkSize), + [](JSContext* ptr) { JS_DestroyContext(ptr); }); + uassert(ErrorCodes::JSInterpreterFailure, "Failed to initialize JSContext", _context); + uassert(ErrorCodes::ExceededMemoryLimit, + "Out of memory while trying to initialize javascript scope", + mallocMemoryLimit == 0 || mongo::sm::get_total_bytes() < mallocMemoryLimit); } MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine) : _engine(engine), _mr(engine), + _runtime(_mr._runtime.get()), _context(_mr._context.get()), _globalProto(_context), _global(_globalProto.getProto()), @@ -426,13 +481,16 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine) // The default is quite low and doesn't seem to directly correlate with // malloc'd bytes. Set it to MAX_INT here and catching things in the // jscustomallocator.cpp - JS_SetGCParameter(_context, JSGC_MAX_BYTES, 0xffffffff); + JS_SetGCParameter(_runtime, JSGC_MAX_BYTES, 0xffffffff); - JS_AddInterruptCallback(_context, _interruptCallback); - JS_SetGCCallback(_context, _gcCallback, this); + JS_SetInterruptCallback(_runtime, _interruptCallback); + JS_SetGCCallback(_runtime, _gcCallback, this); JS_SetContextPrivate(_context, this); + JS_SetRuntimePrivate(_runtime, this); JSAutoRequest ar(_context); + JS_SetErrorReporter(_runtime, _reportError); + JSAutoCompartment ac(_context, _global); _checkErrorState(JS_InitStandardClasses(_context, _global)); @@ -607,8 +665,7 @@ BSONObj MozJSImplScope::callThreadArgs(const BSONObj& args) { for (int i = 0; i < argc; ++i) { ValueReader(_context, &value).fromBSONElement(*it, args, true); - if (!argv.append(value)) - uasserted(ErrorCodes::JSInterpreterFailure, "Failed to append property"); + argv.append(value); it.next(); } @@ -675,8 +732,7 @@ int MozJSImplScope::invoke(ScriptingFunction func, JS::RootedValue value(_context); ValueReader(_context, &value).fromBSONElement(next, *argsObject, readOnlyArgs); - if (!args.append(value)) - uasserted(ErrorCodes::JSInterpreterFailure, "Failed to append property"); + args.append(value); } } @@ -775,7 +831,7 @@ void MozJSImplScope::injectNative(const char* field, NativeFunction func, void* void MozJSImplScope::gc() { _pendingGC.store(true); - JS_RequestInterruptCallback(_context); + JS_RequestInterruptCallback(_runtime); } void MozJSImplScope::sleep(Milliseconds ms) { @@ -872,10 +928,9 @@ bool MozJSImplScope::_checkErrorState(bool success, bool reportError, bool asser if (_status.isOK()) { JS::RootedValue excn(_context); if (JS_GetPendingException(_context, &excn) && excn.isObject()) { + str::stream ss; - auto ss = ValueWriter(_context, excn).toString(); auto stackStr = ObjectWrapper(_context, excn).getString(InternedString::stack); - auto status = jsExceptionToStatus(_context, excn, ErrorCodes::JSInterpreterFailure, ss); auto fnameStr = ObjectWrapper(_context, excn).getString(InternedString::fileName); auto lineNum = ObjectWrapper(_context, excn).getNumberInt(InternedString::lineNumber); auto colNum = ObjectWrapper(_context, excn).getNumberInt(InternedString::columnNumber); @@ -889,12 +944,17 @@ bool MozJSImplScope::_checkErrorState(bool success, bool reportError, bool asser stackStr = ss; } - _status = Status(JSExceptionInfo(std::move(stackStr), status), ss); + if (fnameStr != "") { + ss << "[" << fnameStr << ":" << lineNum << ":" << colNum << "] "; + } + ss << ValueWriter(_context, excn).toString(); + _status = { + JSExceptionInfo(std::move(stackStr), Status(ErrorCodes::JSInterpreterFailure, ss)), + ss}; } else { _status = Status(ErrorCodes::UnknownError, "Unknown Failure from JSInterpreter"); } } - JS_ClearPendingException(_context); if (auto extraInfo = _status.extraInfo<JSExceptionInfo>()) { str::stream reasonWithStack; @@ -933,7 +993,7 @@ auto MozJSImplScope::ASANHandles::getThreadASANHandles() -> ASANHandles* { void MozJSImplScope::setOOM() { _hasOutOfMemoryException = true; - JS_RequestInterruptCallback(_context); + JS_RequestInterruptCallback(_runtime); } void MozJSImplScope::setParentStack(std::string parentStack) { |