diff options
author | Jason Carey <jcarey@argv.me> | 2015-07-09 14:05:20 -0400 |
---|---|---|
committer | Jason Carey <jcarey@argv.me> | 2015-07-14 16:15:54 -0400 |
commit | e749ffad9b4fe3110d97f366ebe39e7c9a4edd9d (patch) | |
tree | 927e727645da9bfc80095bb124860e31e58e9d77 /src/mongo/scripting/mozjs/bson.cpp | |
parent | 1af5f44f9ba2b7cff8e0457798b7a25b64e9fe69 (diff) | |
download | mongo-e749ffad9b4fe3110d97f366ebe39e7c9a4edd9d.tar.gz |
SERVER-18531 Integrate SpiderMonkey
Provides SpiderMonkey 38.0.1esr as a JS engine for mongo and mongod.
Diffstat (limited to 'src/mongo/scripting/mozjs/bson.cpp')
-rw-r--r-- | src/mongo/scripting/mozjs/bson.cpp | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/src/mongo/scripting/mozjs/bson.cpp b/src/mongo/scripting/mozjs/bson.cpp new file mode 100644 index 00000000000..308e29d90f3 --- /dev/null +++ b/src/mongo/scripting/mozjs/bson.cpp @@ -0,0 +1,235 @@ +/** + * Copyright (C) 2015 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/bson.h" + +#include <set> + +#include "mongo/scripting/mozjs/idwrapper.h" +#include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/objectwrapper.h" +#include "mongo/scripting/mozjs/valuereader.h" +#include "mongo/scripting/mozjs/valuewriter.h" + +namespace mongo { +namespace mozjs { + +const char* const BSONInfo::className = "BSON"; + +const JSFunctionSpec BSONInfo::freeFunctions[2] = { + MONGO_ATTACH_JS_FUNCTION(bsonWoCompare), JS_FS_END, +}; + +namespace { + +/** + * Holder for bson objects which tracks state for the js wrapper + * + * Basically, we have read only and read/write variants, and a need to manage + * the appearance of mutable state on the read/write versions. + */ +struct BSONHolder { + BSONHolder(const BSONObj& obj, bool ro) + : _obj(obj.getOwned()), _resolved(false), _readOnly(ro), _altered(false) {} + + BSONObj _obj; + bool _resolved; + bool _readOnly; + bool _altered; + std::set<std::string> _removed; +}; + +BSONHolder* getHolder(JSObject* obj) { + return static_cast<BSONHolder*>(JS_GetPrivate(obj)); +} + +} // namespace + +void BSONInfo::make(JSContext* cx, JS::MutableHandleObject obj, BSONObj bson, bool ro) { + auto scope = getScope(cx); + + scope->getBsonProto().newInstance(obj); + JS_SetPrivate(obj, new BSONHolder(bson, ro)); +} + +void BSONInfo::finalize(JSFreeOp* fop, JSObject* obj) { + auto holder = getHolder(obj); + + if (!holder) + return; + + delete holder; +} + +void BSONInfo::enumerate(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties) { + auto holder = getHolder(obj); + + if (!holder) + return; + + BSONObjIterator i(holder->_obj); + + ObjectWrapper o(cx, obj); + JS::RootedValue val(cx); + JS::RootedId id(cx); + + while (i.more()) { + BSONElement e = i.next(); + + // TODO: when we get heterogenous set lookup, switch to StringData + // rather than involving the temporary string + if (holder->_removed.count(e.fieldName())) + continue; + + ValueReader(cx, &val).fromStringData(e.fieldNameStringData()); + + if (!JS_ValueToId(cx, val, &id)) + uasserted(ErrorCodes::JSInterpreterFailure, "Failed to invoke JS_ValueToId"); + + properties.append(id); + } +} + +void BSONInfo::setProperty( + JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool strict, JS::MutableHandleValue vp) { + auto holder = getHolder(obj); + + if (holder) { + if (holder->_readOnly) { + uasserted(ErrorCodes::BadValue, "Read only object"); + } + + auto iter = holder->_removed.find(IdWrapper(cx, id).toString()); + + if (iter != holder->_removed.end()) { + holder->_removed.erase(iter); + } + + holder->_altered = true; + } + + ObjectWrapper(cx, obj).defineProperty(id, vp, JSPROP_ENUMERATE); +} + +void BSONInfo::delProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* succeeded) { + auto holder = getHolder(obj); + + if (holder) { + if (holder->_readOnly) { + uasserted(ErrorCodes::BadValue, "Read only object"); + } + + holder->_altered = true; + + holder->_removed.insert(IdWrapper(cx, id).toString()); + } + + *succeeded = true; +} + +void BSONInfo::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp) { + auto holder = getHolder(obj); + + *resolvedp = false; + + if (!holder) { + return; + } + + IdWrapper idw(cx, id); + + if (!holder->_readOnly && holder->_removed.count(idw.toString())) { + return; + } + + ObjectWrapper o(cx, obj); + + std::string sname = IdWrapper(cx, id).toString(); + + if (holder->_obj.hasField(sname)) { + auto elem = holder->_obj[sname]; + + JS::RootedValue vp(cx); + + ValueReader(cx, &vp).fromBSONElement(elem, holder->_readOnly); + + o.defineProperty(id, vp, JSPROP_ENUMERATE); + + if (!holder->_readOnly && (elem.type() == mongo::Object || elem.type() == mongo::Array)) { + // if accessing a subobject, we have no way to know if + // modifications are being made on writable objects + + holder->_altered = true; + } + + *resolvedp = true; + } +} + +void BSONInfo::construct(JSContext* cx, JS::CallArgs args) { + auto scope = getScope(cx); + + scope->getBsonProto().newObject(args.rval()); +} + +std::tuple<BSONObj*, bool> BSONInfo::originalBSON(JSContext* cx, JS::HandleObject obj) { + std::tuple<BSONObj*, bool> out(nullptr, false); + + if (auto holder = getHolder(obj)) + out = std::make_tuple(&holder->_obj, holder->_altered); + + return out; +} + +void BSONInfo::Functions::bsonWoCompare(JSContext* cx, JS::CallArgs args) { + if (args.length() != 2) + uasserted(ErrorCodes::BadValue, "bsonWoCompare needs 2 argument"); + + if (!args.get(0).isObject()) + uasserted(ErrorCodes::BadValue, "first argument to bsonWoCompare must be an object"); + + if (!args.get(1).isObject()) + uasserted(ErrorCodes::BadValue, "second argument to bsonWoCompare must be an object"); + + BSONObj firstObject = ValueWriter(cx, args.get(0)).toBSON(); + BSONObj secondObject = ValueWriter(cx, args.get(1)).toBSON(); + + args.rval().setInt32(firstObject.woCompare(secondObject)); +} + +void BSONInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) { + JS::RootedValue value(cx); + value.setBoolean(true); + + ObjectWrapper(cx, proto).defineProperty("_bson", value, 0); +} + +} // namespace mozjs +} // namespace mongo |