summaryrefslogtreecommitdiff
path: root/src/mongo/scripting
diff options
context:
space:
mode:
authorJason Carey <jcarey@argv.me>2017-12-14 14:34:55 -0500
committerJason Carey <jcarey@argv.me>2018-01-19 15:49:17 -0500
commit1c629fb3e0cfdf218a6cdb20882806e3b7dd9e9c (patch)
tree1c0d97ced5b4384754c82b8dc59a360431686a01 /src/mongo/scripting
parentd055ce30c42cec1171928f49c1739c0b14e0b455 (diff)
downloadmongo-1c629fb3e0cfdf218a6cdb20882806e3b7dd9e9c.tar.gz
SERVER-32239 Lossless status throwing in JS
Enable throwing of status without conversion to error reports in spidermonkey. This will enable throwing of sidecars and other rich data through js land.
Diffstat (limited to 'src/mongo/scripting')
-rw-r--r--src/mongo/scripting/SConscript1
-rw-r--r--src/mongo/scripting/mozjs/exception.cpp64
-rw-r--r--src/mongo/scripting/mozjs/exception.h12
-rw-r--r--src/mongo/scripting/mozjs/implscope.cpp18
-rw-r--r--src/mongo/scripting/mozjs/implscope.h13
-rw-r--r--src/mongo/scripting/mozjs/internedstring.defs21
-rw-r--r--src/mongo/scripting/mozjs/objectwrapper.cpp24
-rw-r--r--src/mongo/scripting/mozjs/objectwrapper.h14
-rw-r--r--src/mongo/scripting/mozjs/status.cpp116
-rw-r--r--src/mongo/scripting/mozjs/status.h68
-rw-r--r--src/mongo/scripting/mozjs/wraptype.h15
11 files changed, 315 insertions, 51 deletions
diff --git a/src/mongo/scripting/SConscript b/src/mongo/scripting/SConscript
index 1a91ce45af7..33ab0e8b161 100644
--- a/src/mongo/scripting/SConscript
+++ b/src/mongo/scripting/SConscript
@@ -140,6 +140,7 @@ if usemozjs:
'mozjs/proxyscope.cpp',
'mozjs/regexp.cpp',
'mozjs/session.cpp',
+ 'mozjs/status.cpp',
'mozjs/timestamp.cpp',
'mozjs/uri.cpp',
'mozjs/valuereader.cpp',
diff --git a/src/mongo/scripting/mozjs/exception.cpp b/src/mongo/scripting/mozjs/exception.cpp
index 410fce55b3d..d32ea8eb55a 100644
--- a/src/mongo/scripting/mozjs/exception.cpp
+++ b/src/mongo/scripting/mozjs/exception.cpp
@@ -64,18 +64,10 @@ MONGO_STATIC_ASSERT_MSG(
void mongoToJSException(JSContext* cx) {
auto status = exceptionToStatus();
- auto callback =
- status.code() == ErrorCodes::JSUncatchableError ? uncatchableErrorCallback : errorCallback;
+ JS::RootedValue val(cx);
+ statusToJSException(cx, status, &val);
- JS_ReportErrorNumber(
- cx, callback, nullptr, JSErr_Limit + status.code(), status.reason().c_str());
-}
-
-void setJSException(JSContext* cx, ErrorCodes::Error code, StringData sd) {
- auto callback =
- code == ErrorCodes::JSUncatchableError ? uncatchableErrorCallback : errorCallback;
-
- JS_ReportErrorNumber(cx, callback, nullptr, JSErr_Limit + code, sd.rawData());
+ JS_SetPendingException(cx, val);
}
std::string currentJSStackToString(JSContext* cx) {
@@ -92,16 +84,7 @@ Status currentJSExceptionToStatus(JSContext* cx, ErrorCodes::Error altCode, Stri
if (!JS_GetPendingException(cx, &vp))
return Status(altCode, altReason.rawData());
- if (!vp.isObject()) {
- return Status(altCode, ValueWriter(cx, vp).toString());
- }
-
- JS::RootedObject obj(cx, vp.toObjectOrNull());
- JSErrorReport* report = JS_ErrorFromException(cx, obj);
- if (!report)
- return Status(altCode, altReason.rawData());
-
- return JSErrorReportToStatus(cx, report, altCode, altReason);
+ return jsExceptionToStatus(cx, vp, altCode, altReason);
}
Status JSErrorReportToStatus(JSContext* cx,
@@ -119,11 +102,7 @@ Status JSErrorReportToStatus(JSContext* cx,
error = ErrorCodes::JSInterpreterFailure;
} else {
error = ErrorCodes::Error(report->errorNumber - JSErr_Limit);
- if (ErrorCodes::shouldHaveExtraInfo(error)) {
- // For now we can't propagate extra info through js exceptions.
- // TODO SERVER-32239 delete this block.
- error = ErrorCodes::UnknownError;
- }
+ invariant(!ErrorCodes::shouldHaveExtraInfo(error));
}
}
@@ -135,5 +114,38 @@ void throwCurrentJSException(JSContext* cx, ErrorCodes::Error altCode, StringDat
MONGO_UNREACHABLE;
}
+/**
+ * Turns a status into a js exception
+ */
+void statusToJSException(JSContext* cx, Status status, JS::MutableHandleValue out) {
+ MongoStatusInfo::fromStatus(cx, std::move(status), out);
+}
+
+/**
+ * Turns a js exception into a status
+ */
+Status jsExceptionToStatus(JSContext* cx,
+ JS::HandleValue excn,
+ ErrorCodes::Error altCode,
+ StringData altReason) {
+ auto scope = getScope(cx);
+
+ if (!excn.isObject()) {
+ return Status(altCode, ValueWriter(cx, excn).toString());
+ }
+
+ if (scope->getProto<MongoStatusInfo>().instanceOf(excn)) {
+ return MongoStatusInfo::toStatus(cx, excn);
+ }
+
+ JS::RootedObject obj(cx, excn.toObjectOrNull());
+
+ JSErrorReport* report = JS_ErrorFromException(cx, obj);
+ if (!report)
+ return Status(altCode, altReason.rawData());
+
+ return JSErrorReportToStatus(cx, report, altCode, altReason);
+}
+
} // namespace mozjs
} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/exception.h b/src/mongo/scripting/mozjs/exception.h
index 820e389f0e0..d6a628b6dac 100644
--- a/src/mongo/scripting/mozjs/exception.h
+++ b/src/mongo/scripting/mozjs/exception.h
@@ -43,9 +43,17 @@ namespace mozjs {
void mongoToJSException(JSContext* cx);
/**
- * Sets an exception for javascript
+ * Turns a status into a js exception
*/
-void setJSException(JSContext* cx, ErrorCodes::Error code, StringData sd);
+void statusToJSException(JSContext* cx, Status status, JS::MutableHandleValue out);
+
+/**
+ * Turns a status into a js exception
+ */
+Status jsExceptionToStatus(JSContext* cx,
+ JS::HandleValue excn,
+ ErrorCodes::Error altCode,
+ StringData altReason);
/**
* Converts the current pending js expection into a status
diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp
index b21fd817fad..2c120610008 100644
--- a/src/mongo/scripting/mozjs/implscope.cpp
+++ b/src/mongo/scripting/mozjs/implscope.cpp
@@ -126,6 +126,7 @@ void MozJSImplScope::_reportError(JSContext* cx, const char* message, JSErrorRep
try {
str::stream ss;
+
ss << message;
// TODO: something far more elaborate that mimics the stack printing from v8
@@ -133,7 +134,9 @@ void MozJSImplScope::_reportError(JSContext* cx, const char* message, JSErrorRep
if (JS_GetPendingException(cx, &excn) && excn.isObject()) {
JS::RootedValue stack(cx);
- ObjectWrapper(cx, excn).getValue("stack", &stack);
+ JS::RootedObject obj(cx, excn.toObjectOrNull());
+ ObjectWrapper o(cx, obj);
+ o.getValue("stack", &stack);
auto str = ValueWriter(cx, stack).toString();
@@ -143,6 +146,12 @@ void MozJSImplScope::_reportError(JSContext* cx, const char* message, JSErrorRep
} else {
ss << " :\n" << str;
}
+
+ scope->_status =
+ jsExceptionToStatus(cx, excn, ErrorCodes::JSInterpreterFailure, message)
+ .withReason(ss);
+
+ return;
}
exceptionMsg = ss;
@@ -151,7 +160,6 @@ void MozJSImplScope::_reportError(JSContext* cx, const char* message, JSErrorRep
log() << exceptionMsg << ":" << dbe.toString() << ":" << message;
}
- // TODO SERVER-32239 is this right?
scope->_status =
JSErrorReportToStatus(cx, report, ErrorCodes::JSInterpreterFailure, message)
.withReason(exceptionMsg);
@@ -437,6 +445,7 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine)
_oidProto(_context),
_regExpProto(_context),
_sessionProto(_context),
+ _statusProto(_context),
_timestampProto(_context),
_uriProto(_context) {
kCurrentScope = this;
@@ -850,23 +859,24 @@ void MozJSImplScope::reset() {
}
void MozJSImplScope::installBSONTypes() {
+ _objectProto.install(_global);
+ _errorProto.install(_global);
_binDataProto.install(_global);
_bsonProto.install(_global);
_codeProto.install(_global);
_dbPointerProto.install(_global);
_dbRefProto.install(_global);
- _errorProto.install(_global);
_maxKeyProto.install(_global);
_minKeyProto.install(_global);
_nativeFunctionProto.install(_global);
_numberIntProto.install(_global);
_numberLongProto.install(_global);
_numberDecimalProto.install(_global);
- _objectProto.install(_global);
_oidProto.install(_global);
_regExpProto.install(_global);
_timestampProto.install(_global);
_uriProto.install(_global);
+ _statusProto.install(_global);
// This builtin map is a javascript 6 thing. We want our version. so
// take theirs out
diff --git a/src/mongo/scripting/mozjs/implscope.h b/src/mongo/scripting/mozjs/implscope.h
index 8ae4c6ab3d5..cba83cba184 100644
--- a/src/mongo/scripting/mozjs/implscope.h
+++ b/src/mongo/scripting/mozjs/implscope.h
@@ -60,6 +60,7 @@
#include "mongo/scripting/mozjs/oid.h"
#include "mongo/scripting/mozjs/regexp.h"
#include "mongo/scripting/mozjs/session.h"
+#include "mongo/scripting/mozjs/status.h"
#include "mongo/scripting/mozjs/timestamp.h"
#include "mongo/scripting/mozjs/uri.h"
#include "mongo/stdx/unordered_set.h"
@@ -294,6 +295,12 @@ public:
}
template <typename T>
+ typename std::enable_if<std::is_same<T, MongoStatusInfo>::value, WrapType<T>&>::type
+ getProto() {
+ return _statusProto;
+ }
+
+ template <typename T>
typename std::enable_if<std::is_same<T, TimestampInfo>::value, WrapType<T>&>::type getProto() {
return _timestampProto;
}
@@ -303,6 +310,11 @@ public:
return _uriProto;
}
+ template <typename T>
+ typename std::enable_if<std::is_same<T, GlobalInfo>::value, WrapType<T>&>::type getProto() {
+ return _globalProto;
+ }
+
static const char* const kExecResult;
static const char* const kInvokeResult;
@@ -444,6 +456,7 @@ private:
WrapType<OIDInfo> _oidProto;
WrapType<RegExpInfo> _regExpProto;
WrapType<SessionInfo> _sessionProto;
+ WrapType<MongoStatusInfo> _statusProto;
WrapType<TimestampInfo> _timestampProto;
WrapType<URIInfo> _uriProto;
};
diff --git a/src/mongo/scripting/mozjs/internedstring.defs b/src/mongo/scripting/mozjs/internedstring.defs
index 2dbcb1bb44a..3735c576b97 100644
--- a/src/mongo/scripting/mozjs/internedstring.defs
+++ b/src/mongo/scripting/mozjs/internedstring.defs
@@ -10,14 +10,17 @@ MONGO_MOZJS_INTERNED_STRING(bottom, "bottom")
MONGO_MOZJS_INTERNED_STRING(_bson, "_bson")
MONGO_MOZJS_INTERNED_STRING(code, "code")
MONGO_MOZJS_INTERNED_STRING(_collection, "_collection")
+MONGO_MOZJS_INTERNED_STRING(columnNumber, "columnNumber")
MONGO_MOZJS_INTERNED_STRING(constructor, "constructor")
MONGO_MOZJS_INTERNED_STRING(_cursor, "_cursor")
+MONGO_MOZJS_INTERNED_STRING(database, "database")
MONGO_MOZJS_INTERNED_STRING(_db, "_db")
MONGO_MOZJS_INTERNED_STRING(defaultDB, "defaultDB")
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(fileName, "fileName")
MONGO_MOZJS_INTERNED_STRING(flags, "flags")
MONGO_MOZJS_INTERNED_STRING(floatApprox, "floatApprox")
MONGO_MOZJS_INTERNED_STRING(_fullName, "_fullName")
@@ -26,9 +29,11 @@ MONGO_MOZJS_INTERNED_STRING(host, "host")
MONGO_MOZJS_INTERNED_STRING(_id, "_id")
MONGO_MOZJS_INTERNED_STRING(id, "id")
MONGO_MOZJS_INTERNED_STRING(i, "i")
+MONGO_MOZJS_INTERNED_STRING(isValid, "isValid")
MONGO_MOZJS_INTERNED_STRING(_JSThreadConfig, "_JSThreadConfig")
MONGO_MOZJS_INTERNED_STRING(len, "len")
MONGO_MOZJS_INTERNED_STRING(_limit, "_limit")
+MONGO_MOZJS_INTERNED_STRING(lineNumber, "lineNumber")
MONGO_MOZJS_INTERNED_STRING(MaxKey, "MaxKey")
MONGO_MOZJS_INTERNED_STRING(MinKey, "MinKey")
MONGO_MOZJS_INTERNED_STRING(_mongo, "_mongo")
@@ -38,30 +43,26 @@ 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(options, "options")
+MONGO_MOZJS_INTERNED_STRING(password, "password")
MONGO_MOZJS_INTERNED_STRING(prototype, "prototype")
MONGO_MOZJS_INTERNED_STRING(_query, "_query")
MONGO_MOZJS_INTERNED_STRING(readOnly, "readOnly")
+MONGO_MOZJS_INTERNED_STRING(reason, "reason")
MONGO_MOZJS_INTERNED_STRING(_ro, "_ro")
MONGO_MOZJS_INTERNED_STRING(scope, "scope")
+MONGO_MOZJS_INTERNED_STRING(servers, "servers")
+MONGO_MOZJS_INTERNED_STRING(setName, "setName")
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(stack, "stack")
MONGO_MOZJS_INTERNED_STRING(str, "str")
MONGO_MOZJS_INTERNED_STRING(top, "top")
MONGO_MOZJS_INTERNED_STRING(t, "t")
MONGO_MOZJS_INTERNED_STRING(type, "type")
MONGO_MOZJS_INTERNED_STRING(uri, "uri")
MONGO_MOZJS_INTERNED_STRING(user, "user")
-MONGO_MOZJS_INTERNED_STRING(password, "password")
-MONGO_MOZJS_INTERNED_STRING(options, "options")
-MONGO_MOZJS_INTERNED_STRING(database, "database")
-MONGO_MOZJS_INTERNED_STRING(isValid, "isValid")
-MONGO_MOZJS_INTERNED_STRING(setName, "setName")
-MONGO_MOZJS_INTERNED_STRING(servers, "servers")
-MONGO_MOZJS_INTERNED_STRING(stack, "stack")
-MONGO_MOZJS_INTERNED_STRING(fileName, "fileName")
-MONGO_MOZJS_INTERNED_STRING(lineNumber, "lineNumber")
-MONGO_MOZJS_INTERNED_STRING(columnNumber, "columnNumber")
diff --git a/src/mongo/scripting/mozjs/objectwrapper.cpp b/src/mongo/scripting/mozjs/objectwrapper.cpp
index 98f8094dfcc..0480cc65561 100644
--- a/src/mongo/scripting/mozjs/objectwrapper.cpp
+++ b/src/mongo/scripting/mozjs/objectwrapper.cpp
@@ -107,27 +107,29 @@ void ObjectWrapper::Key::set(JSContext* cx, JS::HandleObject o, JS::HandleValue
void ObjectWrapper::Key::define(JSContext* cx,
JS::HandleObject o,
JS::HandleValue value,
- unsigned attrs) {
+ unsigned attrs,
+ JSNative getter,
+ JSNative setter) {
switch (_type) {
case Type::Field:
- if (JS_DefineProperty(cx, o, _field, value, attrs))
+ if (JS_DefineProperty(cx, o, _field, value, attrs, getter, setter))
return;
break;
case Type::Index:
- if (JS_DefineElement(cx, o, _idx, value, attrs))
+ if (JS_DefineElement(cx, o, _idx, value, attrs, getter, setter))
return;
break;
case Type::Id: {
JS::RootedId id(cx, _id);
- if (JS_DefinePropertyById(cx, o, id, value, attrs))
+ if (JS_DefinePropertyById(cx, o, id, value, attrs, getter, setter))
return;
break;
}
case Type::InternedString: {
InternedStringId id(cx, _internedString);
- if (JS_DefinePropertyById(cx, o, id, value, attrs))
+ if (JS_DefinePropertyById(cx, o, id, value, attrs, getter, setter))
return;
break;
}
@@ -379,8 +381,16 @@ void ObjectWrapper::setObject(Key key, JS::HandleObject object) {
setValue(key, value);
}
-void ObjectWrapper::defineProperty(Key key, JS::HandleValue val, unsigned attrs) {
- key.define(_context, _object, val, attrs);
+void ObjectWrapper::setPrototype(JS::HandleObject object) {
+ if (JS_SetPrototype(_context, _object, object))
+ return;
+
+ throwCurrentJSException(_context, ErrorCodes::InternalError, "Failed to set prototype");
+}
+
+void ObjectWrapper::defineProperty(
+ Key key, JS::HandleValue val, unsigned attrs, JSNative getter, JSNative setter) {
+ key.define(_context, _object, val, attrs, getter, setter);
}
void ObjectWrapper::deleteProperty(Key key) {
diff --git a/src/mongo/scripting/mozjs/objectwrapper.h b/src/mongo/scripting/mozjs/objectwrapper.h
index cf6c61d39e1..a4ab120b1ad 100644
--- a/src/mongo/scripting/mozjs/objectwrapper.h
+++ b/src/mongo/scripting/mozjs/objectwrapper.h
@@ -84,7 +84,12 @@ public:
void set(JSContext* cx, JS::HandleObject o, JS::HandleValue value);
bool has(JSContext* cx, JS::HandleObject o);
bool hasOwn(JSContext* cx, JS::HandleObject o);
- void define(JSContext* cx, JS::HandleObject o, JS::HandleValue value, unsigned attrs);
+ void define(JSContext* cx,
+ JS::HandleObject o,
+ JS::HandleValue value,
+ unsigned attrs,
+ JSNative getter,
+ JSNative setter);
void del(JSContext* cx, JS::HandleObject o);
std::string toString(JSContext* cx);
StringData toStringData(JSContext* cx, JSStringWrapper* jsstr);
@@ -118,11 +123,16 @@ public:
void setBSONArray(Key key, const BSONObj& obj, bool readOnly);
void setValue(Key key, JS::HandleValue value);
void setObject(Key key, JS::HandleObject value);
+ void setPrototype(JS::HandleObject value);
/**
* See JS_DefineProperty for what sort of attributes might be useful
*/
- void defineProperty(Key key, JS::HandleValue value, unsigned attrs);
+ void defineProperty(Key key,
+ JS::HandleValue value,
+ unsigned attrs,
+ JSNative getter = nullptr,
+ JSNative setter = nullptr);
void deleteProperty(Key key);
diff --git a/src/mongo/scripting/mozjs/status.cpp b/src/mongo/scripting/mozjs/status.cpp
new file mode 100644
index 00000000000..cb4ecb1bbd3
--- /dev/null
+++ b/src/mongo/scripting/mozjs/status.cpp
@@ -0,0 +1,116 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/status.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/internedstring.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/wrapconstrainedmethod.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const MongoStatusInfo::className = "MongoStatus";
+const char* const MongoStatusInfo::inheritFrom = "Error";
+
+Status MongoStatusInfo::toStatus(JSContext* cx, JS::HandleObject object) {
+ return *static_cast<Status*>(JS_GetPrivate(object));
+}
+
+Status MongoStatusInfo::toStatus(JSContext* cx, JS::HandleValue value) {
+ return *static_cast<Status*>(JS_GetPrivate(value.toObjectOrNull()));
+}
+
+void MongoStatusInfo::fromStatus(JSContext* cx, Status status, JS::MutableHandleValue value) {
+ auto scope = getScope(cx);
+
+ JS::RootedValue undef(cx);
+ undef.setUndefined();
+
+ JS::AutoValueArray<1> args(cx);
+ ValueReader(cx, args[0]).fromStringData(status.reason());
+ JS::RootedObject error(cx);
+ scope->getProto<ErrorInfo>().newInstance(args, &error);
+
+ JS::RootedObject thisv(cx);
+ scope->getProto<MongoStatusInfo>().newObjectWithProto(&thisv, error);
+ ObjectWrapper thisvObj(cx, thisv);
+ thisvObj.defineProperty(
+ InternedString::code,
+ undef,
+ JSPROP_ENUMERATE | JSPROP_SHARED,
+ smUtils::wrapConstrainedMethod<Functions::code, false, MongoStatusInfo>);
+
+ thisvObj.defineProperty(
+ InternedString::reason,
+ undef,
+ JSPROP_ENUMERATE | JSPROP_SHARED,
+ smUtils::wrapConstrainedMethod<Functions::reason, false, MongoStatusInfo>);
+
+ JS_SetPrivate(thisv, scope->trackedNew<Status>(std::move(status)));
+
+ value.setObjectOrNull(thisv);
+}
+
+void MongoStatusInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto code = args.get(0).toInt32();
+ auto reason = JSStringWrapper(cx, args.get(1).toString()).toString();
+
+ JS::RootedValue out(cx);
+ fromStatus(cx, Status(ErrorCodes::Error(code), std::move(reason)), &out);
+
+ args.rval().set(out);
+}
+
+void MongoStatusInfo::finalize(JSFreeOp* fop, JSObject* obj) {
+ auto status = static_cast<Status*>(JS_GetPrivate(obj));
+
+ if (status)
+ getScope(fop)->trackedDelete(status);
+}
+
+void MongoStatusInfo::Functions::code::call(JSContext* cx, JS::CallArgs args) {
+ args.rval().setInt32(toStatus(cx, args.thisv()).code());
+}
+
+void MongoStatusInfo::Functions::reason::call(JSContext* cx, JS::CallArgs args) {
+ ValueReader(cx, args.rval()).fromStringData(toStatus(cx, args.thisv()).reason());
+}
+
+void MongoStatusInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) {
+ auto scope = getScope(cx);
+
+ JS_SetPrivate(proto, scope->trackedNew<Status>(Status::OK()));
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/status.h b/src/mongo/scripting/mozjs/status.h
new file mode 100644
index 00000000000..d930bdb8df9
--- /dev/null
+++ b/src/mongo/scripting/mozjs/status.h
@@ -0,0 +1,68 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "MongoStatus" Javascript object.
+ *
+ * This type wraps the "Status" type in the server, allowing for lossless throwing of mongodb native
+ * exceptions through javascript. It can be created (albeit without sidecar) from javascript.
+ * These are also created automatically when exceptions are thrown from native c++ functions.
+ *
+ * They are somewhat special, in that the prototype for each MongoStatus object is actually an Error
+ * object specific to that status object. This allows Error-like behavior such as useful stack
+ * traces, and instanceOf Error.
+ */
+struct MongoStatusInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+ static void finalize(JSFreeOp* fop, JSObject* obj);
+
+ struct Functions {
+ MONGO_DECLARE_JS_FUNCTION(code);
+ MONGO_DECLARE_JS_FUNCTION(reason);
+ };
+
+ static void postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto);
+
+ static const char* const className;
+ static const char* const inheritFrom;
+ static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
+
+ static Status toStatus(JSContext* cx, JS::HandleObject object);
+ static Status toStatus(JSContext* cx, JS::HandleValue value);
+ static void fromStatus(JSContext* cx, Status status, JS::MutableHandleValue value);
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/wraptype.h b/src/mongo/scripting/mozjs/wraptype.h
index 76af28efd1c..4f85802d4ec 100644
--- a/src/mongo/scripting/mozjs/wraptype.h
+++ b/src/mongo/scripting/mozjs/wraptype.h
@@ -298,6 +298,17 @@ public:
out.setObjectOrNull(obj);
}
+ void newObjectWithProto(JS::MutableHandleObject out, JS::HandleObject proto) {
+ out.set(_assertPtr(JS_NewObjectWithGivenProto(_context, &_jsclass, proto)));
+ }
+
+ void newObjectWithProto(JS::MutableHandleValue out, JS::HandleObject proto) {
+ JS::RootedObject obj(_context);
+ newObjectWithProto(&obj, proto);
+
+ out.setObjectOrNull(obj);
+ }
+
/**
* newInstance calls the constructor, a la new Type() in js
*/
@@ -353,6 +364,10 @@ public:
return _proto;
}
+ JS::HandleObject getCtor() const {
+ return _constructor;
+ }
+
private:
/**
* Use this if you want your types installed visibly in the global scope